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;
}
답변
답변
영구 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 생성자없이 포함 가능을 지원하는 경우, 당신은 추가 할 수 있습니다
@Embeddable
에PersistentEnumSet
(그리고 별도의 클래스를 삭제... interests = new PersistentEnumSet<>(InterestsEnum.class);
)
- 당신의 지속성 공급자가 public 생성자없이 포함 가능을 지원하는 경우, 당신은 추가 할 수 있습니다
- 엔터티에
@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 필드와 외래 키가 필요합니다. 문자열에 쉼표로 구분 된 목록을 저장하는 것과 같은 미친 짓을하지 않는 한 (하지 마십시오!)