Я использую небольшую модификацию java.util.RegularEnumSet, чтобы иметь постоянный 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
// ...
}
Этот класс теперь является базой для всех моих наборов перечислений:
@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();
}
Преимущества:
- Работа с безопасным и производным перечислением enum, установленным в вашем коде (см.
java.util.EnumSet
для описания)
- Набор представляет собой один числовой столбец в базе данных
- все просто JPA (не зависит от поставщика пользовательские типы )
- простое (и краткое) объявление новых полей того же типа по сравнению с другими решениями
Недостатки:
- Дублирование кода (
RegularEnumSet
и PersistentEnumSet
почти одинаковы)
- Вы можете заключить результат
EnumSet.noneOf(enumType)
в свой PersistenEnumSet
, объявить AccessType.PROPERTY
и предоставить два метода доступа, которые используют отражение для чтения и записи elements
поля
- Для каждого перечислимого класса, который должен храниться в постоянном наборе, необходим дополнительный набор классов.
- Если ваш поставщик сохраняемости поддерживает встраиваемые возможности без открытого конструктора, вы можете добавить
@Embeddable
к PersistentEnumSet
и удалить
дополнительный класс (... interests = new PersistentEnumSet<>(InterestsEnum.class);
)
- Вы должны использовать
@AttributeOverride
, как указано в моем примере, если у вас есть более одного PersistentEnumSet
в вашей сущности (в противном случае оба элемента будут сохранены в одном и том же столбце "элементы")
- Доступ к
values()
с отражением в конструкторе не является оптимальным (особенно если смотреть на производительность), но два других варианта также имеют свои недостатки:
- Реализация типа
EnumSet.getUniverse()
использует sun.misc
класс
- Предоставление массива значений в качестве параметра может привести к тому, что указанные значения будут неправильными
- Поддерживаются только перечисления до 64 значений (это действительно недостаток?)
- Вместо этого вы можете использовать BigInteger
- Нелегко использовать поле элементов в запросе критериев или JPQL
- Вы можете использовать бинарные операторы или столбец битовой маски с соответствующими функциями, если ваша база данных поддерживает это