using Hibernate you can do
@CollectionOfElements(targetElement = InterestsEnum.class)
@JoinTable(name = "tblInterests", joinColumns = @JoinColumn(name = "personID"))
@Column(name = "interest", nullable = false)
@Enumerated(EnumType.STRING)
Collection<InterestsEnum> interests;
The link in Andy's answer is a great starting point for mapping collections of "non-Entity" objects in JPA 2, but isn't quite complete when it comes to mapping enums. Here is what I came up with instead.
@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;
}
I was able to accomplish this in this simple way:
@ElementCollection(fetch = FetchType.EAGER)
Collection<InterestsEnum> interests;
Eager loading is required in order to avoid lazy loading inizializing error as explained here.
tl;dr A short solution would be the following:
@ElementCollection(targetClass = InterestsEnum.class)
@CollectionTable
@Enumerated(EnumType.STRING)
Collection<InterestsEnum> interests;
The long answer is that with this annotations JPA will create one table that will hold the list of InterestsEnum pointing to the main class identifier (Person.class in this case).
@ElementCollections specify where JPA can find information about the Enum
@CollectionTable create the table that hold relationship from Person to InterestsEnum
@Enumerated(EnumType.STRING) tell JPA to persist the Enum as String, could be EnumType.ORDINAL
I'm using a slight modification of java.util.RegularEnumSet to have a persistent 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");
}
}
// 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:
java.util.EnumSet
for a description)Drawbacks:
RegularEnumSet
and PersistentEnumSet
are nearly the same)
EnumSet.noneOf(enumType)
in your PersistenEnumSet
, declare AccessType.PROPERTY
and provide two access methods which use reflection to read and write the elements
field@Embeddable
to PersistentEnumSet
and drop the
extra class (... interests = new PersistentEnumSet<>(InterestsEnum.class);
)@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")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:
EnumSet.getUniverse()
makes use of a sun.misc
classIf you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With