열거 형의 JPA 맵 컬렉션


93

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

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

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

답변:


112

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

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

141
누군가 지금 이것을 읽으면 ... @CollectionOfElements는 이제 더 이상 사용되지 않습니다. 대신 다음을 사용하십시오. @ElementCollection

2
이 질문의 answser에서 샘플을 찾을 수 있습니다. stackoverflow.com/q/3152787/363573
Stephan

1
Hibernate에 대해 언급 했으므로이 답변이 공급 업체별로 다를 수 있다고 생각했지만 JoinTable을 사용하면 다른 구현에서 문제가 발생하지 않는 한 그렇지 않다고 생각합니다. 내가 본 것에서 CollectionTable을 대신 사용해야한다고 생각합니다. 그것이 내가 내 대답에서 사용한 것이고 그것은 나를 위해 작동합니다 (예, 나는 또한 지금 Hibernate를 사용하고 있습니다.)
spaaarky21

나는 이것이 오래된 스레드라는 것을 알고 있지만 우리는 javax.persistence를 사용하여 같은 종류의 것을 구현하고 있습니다. 추가 할 때 : @ElementCollection (targetClass = Roles.class) @CollectionTable (name = "USER_ROLES", joinColumns = @ JoinColumn (name = "USER_ID")) @Column (name = "ROLE", nullable = false) @Enumerated ( EnumType.STRING) 개인 Set <Roles> 역할; User 테이블에는 전체 모델 패키지에서 모든 것이 분리됩니다. User 개체에서 기본 키의 @Id ... @ GeneratedValue 생성기와 첫 번째 @OneToMany의 오류도 빌드시 구피 오류를 발생시킵니다.
LinuxLars

그만한 가치는-내가보고있는 오류는 버그입니다 -issues.jboss.org/browse/JBIDE-16016
LinuxLars

65

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;
}

4
안타깝게도 일부 "관리자"는 이유를 밝히지 않고 해당 답변을 삭제하기로 결정했습니다 (여기에서 해당 과정에 대한 내용). 참고로 datanucleus.org/products/accessplatform_3_0/jpa/orm/…
DataNucleus 2013

2
실제로이 밖으로 필요가있는 것입니다 @ElementCollection그리고 Collection<InterestsEnum> interests; 나머지는 잠재적으로 유용하지만, 필요하지 않습니다. 예를 들어 @Enumerated(EnumType.STRING)사람이 읽을 수있는 문자열을 데이터베이스에 넣습니다.
CorayThan

2
당신이 맞습니다-이 예에서 당신은 @Column's name가 함축 된 것에 의존 할 수 있습니다. @Column이 생략 될 때 의미하는 바를 명확히하고 싶었습니다. 그리고 @Enumerated는 항상 서수를 기본값으로 사용하는 것이 끔찍한 것이기 때문에 권장됩니다. :)
spaaarky21

나는 당신이 실제로 person_interest 테이블 필요가 있다고 언급에 그 가치를 생각한다
lukaszrys가

1
나는 그것을 작동시키기 위해 joinColumn 매개 변수를 추가해야했다@CollectionTable(name="person_interest", joinColumns = {@JoinColumn(name="person_id")})
Tiago

8

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

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

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


5

영구 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에서 요소 필드를 사용하는 것은 쉽지 않습니다.
    • 데이터베이스에서 지원하는 경우 적절한 함수와 함께 이진 연산자 또는 비트 마스크 열을 사용할 수 있습니다.

4

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 일 수 있습니다.


이 경우 변경 불가능한 PersistenceSet으로 저장되기 때문에이 컬렉션을 변경할 수 없습니다.
Nicolazz92

내 실수. setter로이 세트를 변경할 수 있습니다.
Nicolazz92

0

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


8
이것은 JPA 1.0에만 유효합니다. JPA 2.0에서는 위에 표시된대로 @ElementCollection 주석을 사용할 수 있습니다.
rustyx 2010-12-27
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.