Коллекция карт JPA из Enums
Есть ли способ в JPA сопоставить коллекцию Enums в classе Entity? Или единственным решением является обернуть Enum другим classом домена и использовать его для сопоставления коллекции?
@Entity public class Person { public enum InterestsEnum {Books, Sport, etc... } //@??? Collection interests; }
Я использую Hibernate JPA, но, конечно, предпочла бы реализацию агностического решения.
- В чем разница между интерфейсами CrudRepository и JpaRepository в Spring Data JPA?
- Ошибка проверки: значение недействительно
- @GeneratedValue полиморфный абстрактный суперclass над MySQL
- В чем разница между persist () и merge () в Hibernate?
- Hibernate JPA Sequence (не Id)
- Вычисленное свойство с JPA / Hibernate
- Проблема спящего режима - «Использование @OneToMany или @ManyToMany для таргетинга на немаркированный class»
- Множественные выборки с типом EAGER в Hibernate с JPA
- Hibernate - @ElementCollection - странное поведение удаления / вставки
- Hibernate: индивидуальная ленивая загрузка, необязательно = false
- Каков правильный путь для ссылки jar-файла в jpa persistence.xml в веб-приложении?
- Нужны ли мне элементы в файле persistence.xml?
- Набор карт с помощью @ElementCollection
используя Hibernate, вы можете сделать
@CollectionOfElements(targetElement = InterestsEnum.class) @JoinTable(name = "tblInterests", joinColumns = @JoinColumn(name = "personID")) @Column(name = "interest", nullable = false) @Enumerated(EnumType.STRING) Collection interests;
Ссылка в ответе Энди – отличная отправная точка для сопоставления наборов объектов «не-Entity» в 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 interests; }
Я использую небольшую модификацию java.util.RegularEnumSet, чтобы иметь постоянный 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 // ... }
Этот class теперь является базой для всех моих наборов перечислений:
@Embeddable public class InterestsSet extends PersistentEnumSet { public InterestsSet() { super(InterestsEnum.class); } }
И этот набор, который я могу использовать в своей сущности:
@Entity public class MyEntity { // ... @Embedded @AttributeOverride(name="elements", [email protected](name="interests")) private InterestsSet interests = new InterestsSet(); }
Преимущества:
- Работа с типом безопасного и исполняемого enums, установленного в вашем коде (см.
java.util.EnumSet
для описания) - Набор представляет собой только один числовой столбец в базе данных
- все это простой JPA ( нестандартные типы поставщиков)
- легкое (и короткое) объявление новых полей того же типа, по сравнению с другими решениями
Недостатки:
- Дублирование кода (
RegularEnumSet
иPersistentEnumSet
почти одинаковы)- Вы можете обернуть результат
EnumSet.noneOf(enumType)
в свойPersistenEnumSet
, объявитьAccessType.PROPERTY
и предоставить два метода доступа, которые используют reflection для чтения и записи поляelements
- Вы можете обернуть результат
- Для каждого classа enums, который должен храниться в постоянном наборе, необходим дополнительный class набора
- Если ваш провайдер персистентности поддерживает вложенные
@Embeddable
без общего конструктора, вы можете добавить@Embeddable
вPersistentEnumSet
и удалить дополнительный class (... interests = new PersistentEnumSet<>(InterestsEnum.class);
)
- Если ваш провайдер персистентности поддерживает вложенные
- Вы должны использовать
@AttributeOverride
, как указано в моем примере, если у вас более одногоPersistentEnumSet
в вашей сущности (иначе оба будут сохранены в те же элементы столбца) - Доступ
values()
с reflectionм в конструкторе не является оптимальным (особенно при просмотре производительности), но у двух других вариантов есть свои недостатки:- Реализация, такая как
EnumSet.getUniverse()
использует classsun.misc
- Предоставление массива значений в качестве параметра имеет риск того, что данные значения не являются правильными
- Реализация, такая как
- Поддерживаются только enums с до 64 значениями (это действительно недостаток?)
- Вместо этого вы можете использовать BigInteger
- Нелегко использовать поле элементов в критерии запроса или JPQL
- Вы можете использовать двоичные операторы или битмакс-столбец с соответствующими функциями, если ваша firebase database поддерживает это
Я смог выполнить это простым способом:
@ElementCollection(fetch = FetchType.EAGER) Collection interests;
Желательная загрузка необходима, чтобы избежать ленивой ошибки инициализации загрузки, как описано здесь .
Коллекции в JPA относятся к отношениям один-ко-многим или многим ко многим, и они могут содержать только другие сущности. Извините, но вам нужно будет обернуть эти enums в сущности. Если вы подумаете об этом, вам понадобится какое-то поле идентификатора и внешний ключ, чтобы сохранить эту информацию в любом случае. То есть, если вы не делаете что-то безумное, как хранить список, разделенный запятыми, в String (не делайте этого!).