JPA map collection of Enums

后端 未结 6 694
滥情空心
滥情空心 2020-11-27 12:46

Is there a way in JPA to map a collection of Enums within the Entity class? Or the only solution is to wrap Enum with another domain class and use it to map the collection?<

6条回答
  •  一整个雨季
    2020-11-27 13:23

    I'm using a slight modification of java.util.RegularEnumSet to have a persistent EnumSet:

    @MappedSuperclass
    @Access(AccessType.FIELD)
    public class PersistentEnumSet> 
        extends AbstractSet {
      private long elements;
    
      @Transient
      private final Class elementType;
    
      @Transient
      private final E[] universe;
    
      public PersistentEnumSet(final Class 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 {
      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

提交回复
热议问题