[java] 열거 형의 JPA 맵 컬렉션

JPA에서 Entity 클래스 내에서 Enum 컬렉션을 매핑하는 방법이 있습니까? 아니면 유일한 해결책은 Enum을 다른 도메인 클래스로 래핑하고 컬렉션을 매핑하는 데 사용하는 것입니까?

@Entity
public class Person {
    public enum InterestsEnum {Books, Sport, etc...  }
    //@???
    Collection<InterestsEnum> interests;
}

Hibernate JPA 구현을 사용하고 있지만 물론 구현에 구애받지 않는 솔루션을 선호합니다.



답변

Hibernate를 사용하면 할 수 있습니다.

@CollectionOfElements(targetElement = InterestsEnum.class)
@JoinTable(name = "tblInterests", joinColumns = @JoinColumn(name = "personID"))
@Column(name = "interest", nullable = false)
@Enumerated(EnumType.STRING)
Collection<InterestsEnum> interests;


답변

Andy의 답변에있는 링크는 JPA 2에서 “비 엔티티”개체의 컬렉션을 매핑하는 데 좋은 출발점이지만 열거 형 매핑에 관해서는 완전하지 않습니다. 대신 내가 생각해 낸 것이 있습니다.

@Entity
public class Person {
    @ElementCollection(targetClass=InterestsEnum.class)
    @Enumerated(EnumType.STRING) // Possibly optional (I'm not sure) but defaults to ORDINAL.
    @CollectionTable(name="person_interest")
    @Column(name="interest") // Column name in person_interest
    Collection<InterestsEnum> interests;
}


답변

이 간단한 방법으로이 작업을 수행 할 수있었습니다.

@ElementCollection(fetch = FetchType.EAGER)
Collection<InterestsEnum> interests;

여기에 설명 된대로 지연로드 초기화 오류를 방지하려면 즉시로드가 필요 합니다 .


답변

영구 EnumSet을 갖도록 java.util.RegularEnumSet의 약간의 수정을 사용하고 있습니다.

@MappedSuperclass
@Access(AccessType.FIELD)
public class PersistentEnumSet<E extends Enum<E>>
    extends AbstractSet<E> {
  private long elements;

  @Transient
  private final Class<E> elementType;

  @Transient
  private final E[] universe;

  public PersistentEnumSet(final Class<E> elementType) {
    this.elementType = elementType;
    try {
      this.universe = (E[]) elementType.getMethod("values").invoke(null);
    } catch (final ReflectiveOperationException e) {
      throw new IllegalArgumentException("Not an enum type: " + elementType, e);
    }
    if (this.universe.length > 64) {
      throw new IllegalArgumentException("More than 64 enum elements are not allowed");
    }
  }

  // Copy everything else from java.util.RegularEnumSet
  // ...
}

이 클래스는 이제 모든 열거 형 집합의 기본입니다.

@Embeddable
public class InterestsSet extends PersistentEnumSet<InterestsEnum> {
  public InterestsSet() {
    super(InterestsEnum.class);
  }
}

그리고 그 세트는 내 엔티티에서 사용할 수 있습니다.

@Entity
public class MyEntity {
  // ...
  @Embedded
  @AttributeOverride(name="elements", column=@Column(name="interests"))
  private InterestsSet interests = new InterestsSet();
}

장점 :

  • 코드에 설정된 형식 안전하고 성능이 뛰어난 열거 형 작업 ( java.util.EnumSet설명 은 참조 )
  • 집합은 데이터베이스에서 하나의 숫자 열입니다.
  • 모든 것이 일반 JPA입니다 (공급 업체별 사용자 정의 유형 없음 ).
  • 다른 솔루션에 비해 동일한 유형의 새 필드를 쉽고 짧게 선언

단점 :

  • 코드 복제 ( RegularEnumSet그리고 PersistentEnumSet거의 동일)
    • 당신의 결과 포장 할 수 EnumSet.noneOf(enumType)귀하의 PersistenEnumSet선언을 AccessType.PROPERTY하고 읽을 수있는 반사를 사용하는 두 가지 접근 방법을 제공하고 쓰기 elements필드
  • 영구 집합에 저장해야하는 모든 열거 형 클래스에는 추가 집합 클래스가 필요합니다.
    • 당신의 지속성 공급자가 public 생성자없이 포함 가능을 지원하는 경우, 당신은 추가 할 수 있습니다 @EmbeddablePersistentEnumSet(그리고 별도의 클래스를 삭제 ... interests = new PersistentEnumSet<>(InterestsEnum.class);)
  • 엔터티에 @AttributeOverride둘 이상의 항목이있는 경우 내 예제에 제공된대로 를 사용해야합니다 PersistentEnumSet(그렇지 않으면 둘 다 동일한 열 “요소”에 저장 됨).
  • values()생성자에서 with 리플렉션에 액세스하는 것은 최적이 아니지만 (특히 성능을 볼 때) 다른 두 옵션에도 단점이 있습니다.
    • 클래스 EnumSet.getUniverse()사용 과 같은 구현sun.misc
    • 값 배열을 매개 변수로 제공하면 주어진 값이 올바른 값이 아닐 위험이 있습니다.
  • 최대 64 개의 값이있는 열거 형 만 지원됩니다 (정말 단점입니까?).
    • 대신 BigInteger를 사용할 수 있습니다.
  • 기준 쿼리 또는 JPQL에서 요소 필드를 사용하는 것은 쉽지 않습니다.
    • 데이터베이스에서 지원하는 경우 적절한 함수와 함께 이진 연산자 또는 비트 마스크 열을 사용할 수 있습니다.


답변

tl; dr 짧은 해결책은 다음과 같습니다.

@ElementCollection(targetClass = InterestsEnum.class)
@CollectionTable
@Enumerated(EnumType.STRING)
Collection<InterestsEnum> interests;

긴 대답은이 주석을 사용하여 JPA가 기본 클래스 식별자 (이 경우에는 Person.class)를 가리키는 InterestsEnum 목록을 보유 할 하나의 테이블을 생성한다는 것입니다.

@ElementCollections는 JPA가 Enum에 대한 정보를 찾을 수있는 위치를 지정합니다.

@CollectionTable은 Person에서 InterestsEnum으로의 관계를 유지하는 테이블을 만듭니다.

@Enumerated (EnumType.STRING)은 JPA에게 Enum을 String으로 유지하도록 지시합니다. EnumType.ORDINAL 일 수 있습니다.


답변

JPA의 컬렉션은 일대 다 또는 다 대다 관계를 참조하며 다른 엔터티 만 포함 할 수 있습니다. 죄송합니다. 이러한 열거 형을 엔터티로 래핑해야합니다. 생각해 보면 어쨌든이 정보를 저장하려면 일종의 ID 필드와 외래 키가 필요합니다. 문자열에 쉼표로 구분 된 목록을 저장하는 것과 같은 미친 짓을하지 않는 한 (하지 마십시오!)


답변