I use a small modification of java.util.RegularEnumSet to have a constant EnumSet:
@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"); } }
This class is now the base for all my enumeration sets:
@Embeddable public class InterestsSet extends PersistentEnumSet<InterestsEnum> { public InterestsSet() { super(InterestsEnum.class); } }
And this set that I can use in my essence:
@Entity public class MyEntity { // ... @Embedded @AttributeOverride(name="elements", column=@Column(name="interests")) private InterestsSet interests = new InterestsSet(); }
Benefits:
- Work with the type of safe and executable enumeration installed in your code (see
java.util.EnumSet for a description) - A collection is just one numeric column in a database
- everything is just JPA (non-standard user provider types)
- easy (and short) announcement of new fields of the same type compared to other solutions
Disadvantages:
- Code duplication (
RegularEnumSet and PersistentEnumSet almost the same)- You can wrap the result of
EnumSet.noneOf(enumType) in PersistenEnumSet , declare AccessType.PROPERTY and provide two access methods that use reflection to read and write the elements field
- For each enumeration class to be stored in a persistent set, an additional set class is required
- If your persistence provider supports embedded attachments without a public constructor, you can add
@Embeddable to PersistentEnumSet and remove the extra class ( ... interests = new PersistentEnumSet<>(InterestsEnum.class); )
- You should use
@AttributeOverride as indicated in my example if you have more than one PersistentEnumSet in your entity (otherwise both will be stored in the same column elements) - Accessing
values() with reflection in the constructor is not optimal (especially when viewing performance), but the other two options have their drawbacks:- An implementation similar to
EnumSet.getUniverse() uses the sun.misc class - Providing an array of values ββas a parameter has the risk that the given values ββare not correct
- Only enumerations with up to 64 values ββare supported (is this really a flaw?)
- You can use BigInteger instead
- It is not easy to use an element field in a query of criteria or in JPQL
- You can use binary operators or a bitmax column with the corresponding functions if your database supports this
Tobias Liefke Jan 27 '15 at 10:27 2015-01-27 22:27
source share