Program Tip

열거 형의 JPA 맵 컬렉션

programtip 2020. 10. 9. 12:16
반응형

열거 형의 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
  // ...
}

This class is now the base for all of my enum sets:

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

And that set I can use in my entity:

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

Advantages:

  • Working with a type safe and performant enum set in your code (see java.util.EnumSet for a description)
  • The set is just one numeric column in the database
  • everything is plain JPA (no provider specific custom types)
  • easy (and short) declaration of new fields of the same type, compared with the other solutions

Drawbacks:

  • Code duplication (RegularEnumSet and PersistentEnumSet are nearly the same)
    • You could wrap the result of EnumSet.noneOf(enumType) in your PersistenEnumSet, declare AccessType.PROPERTY and provide two access methods which use reflection to read and write the elements field
  • An additional set class is needed for every enum class that should be stored in a persistent set
    • If your persistence provider supports embeddables without a public constructor, you could add @Embeddable to PersistentEnumSet and drop the extra class (... interests = new PersistentEnumSet<>(InterestsEnum.class);)
  • You must use an @AttributeOverride, as given in my example, if you've got more than one PersistentEnumSet in your entity (otherwise both would be stored to the same column "elements")
  • The access of values() with reflection in the constructor is not optimal (especially when looking at the performance), but the two other options have their drawbacks as well:
    • An implementation like EnumSet.getUniverse() makes use of a sun.misc class
    • Providing the values array as parameter has the risk that the given values are not the correct ones
  • Only enums with up to 64 values are supported (is that really a drawback?)
    • You could use BigInteger instead
  • It's not easy to use the elements field in a criteria query or JPQL
    • You could use binary operators or a bitmask column with the appropriate functions, if your database supports that

Collections in JPA refer to one-to-many or many-to-many relationships and they can only contain other entities. Sorry, but you'd need to wrap those enums in an entity. If you think about it, you'd need some sort of ID field and foreign key to store this information anyway. That is unless you do something crazy like store a comma-separated list in a String (don't do this!).

참고URL : https://stackoverflow.com/questions/416208/jpa-map-collection-of-enums

반응형