Коллекция СПД карту перечислений
есть ли способ в JPA сопоставить коллекцию перечислений в классе сущностей? Или единственное решение-обернуть Enum с другим классом домена и использовать его для сопоставления коллекции?
@Entity
public class Person {
public enum InterestsEnum {Books, Sport, etc... }
//@???
Collection<InterestsEnum> interests;
}
Я использую реализацию Hibernate JPA, но, конечно, предпочел бы реализацию агностического решения.
5 ответов:
С помощью Hibernate вы можете сделать
@CollectionOfElements(targetElement = InterestsEnum.class) @JoinTable(name = "tblInterests", joinColumns = @JoinColumn(name = "personID")) @Column(name = "interest", nullable = false) @Enumerated(EnumType.STRING) Collection<InterestsEnum> interests;
ссылка в ответе Энди является отличной отправной точкой для сопоставления коллекций "не-сущностных" объектов в 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;
требуется активная загрузка, чтобы избежать ошибки ленивой загрузки inizializing, как объяснено здесь.
я использую небольшую модификацию java.утиль.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(); }
плюсы:
- работа с типобезопасным и производительным перечислением, установленным в вашем коде (см.
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
- вы можете использовать двоичные операторы или столбец битовой маски с соответствующими функциями, если ваша база данных поддерживает это
коллекции в JPA относятся к отношениям "один ко многим" или "многие ко многим", и они могут содержать только другие сущности. Извините, но вам нужно будет обернуть эти перечисления в сущность. Если вы подумаете об этом, вам понадобится какое-то поле ID и внешний ключ для хранения этой информации в любом случае. То есть, если вы не делаете что-то сумасшедшее, как хранить список через запятую в строке (не делайте этого!).