Есть ли способ в JPA сопоставить коллекцию перечислений в классе Entity? Или единственное решение - обернуть Enum другим классом домена и использовать его для сопоставления коллекции?
@Entity
public class Person {
public enum InterestsEnum {Books, Sport, etc... }
//@???
Collection<InterestsEnum> interests;
}
Я использую реализацию Hibernate JPA, но, конечно, предпочел бы независимое от реализации решение.




Коллекции в JPA относятся к отношениям «один ко многим» или «многие ко многим» и могут содержать только другие сущности. Извините, но вам нужно обернуть эти перечисления в объект. Если подумать, вам все равно понадобится какое-то поле идентификатора и внешний ключ для хранения этой информации. Это если вы не сделаете что-то безумное, например, сохраните список, разделенный запятыми, в String (не делайте этого!).
используя Hibernate, вы можете сделать
@CollectionOfElements(targetElement = InterestsEnum.class)
@JoinTable(name = "tblInterests", joinColumns = @JoinColumn(name = "personID"))
@Column(name = "interest", nullable = false)
@Enumerated(EnumType.STRING)
Collection<InterestsEnum> interests;
На случай, если кто-нибудь прочитает это сейчас ... @CollectionOfElements устарела, вместо этого используйте: @ElementCollection
Вы можете найти образец в ответе на этот вопрос: stackoverflow.com/q/3152787/363573
Поскольку вы упомянули Hibernate, я подумал, что этот ответ может зависеть от поставщика, но я не думаю, что это так, если использование JoinTable не вызывает проблем в других реализациях. Из того, что я видел, я считаю, что вместо этого следует использовать CollectionTable. Это то, что я использовал в своем ответе, и это работает для меня (хотя да, я также использую Hibernate прямо сейчас.)
Я знаю, что это старый поток, но мы реализуем то же самое, используя javax.persistence. Когда мы добавляем: @ElementCollection (targetClass = Roles.class) @CollectionTable (name = "USER_ROLES", joinColumns = @ JoinColumn (name = "USER_ID")) @Column (name = "ROLE", nullable = false) @Enumerated ( EnumType.STRING) private Установить роли <Roles>; к нашей таблице User, во всем модельном пакете все идет не так. В нашем объекте User даже ошибка в генераторе @Id ... @ GeneratedValue первичного ключа и первый @OneToMany выдают глупые ошибки при сборке.
Для чего это стоит - ошибки, которые я вижу, это баг - issues.jboss.org/browse/JBIDE-16016
Ссылка в ответе Энди - отличная отправная точка для сопоставления коллекций "не-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<InterestsEnum> interests;
}
К сожалению, какой-то «админ» решил удалить этот ответ без объяснения причин (примерно по номинальной стоимости здесь). Для справки это datanucleus.org/products/accessplatform_3_0/jpa/orm/…
Все, что вам действительно нужно, это @ElementCollection и Collection<InterestsEnum> interests;. Остальное потенциально полезно, но не нужно. Например, @Enumerated(EnumType.STRING) помещает в вашу базу данных удобочитаемые строки.
Вы правы - в этом примере вы можете полагаться на подразумеваемый @Columnname. Я просто хотел прояснить, что подразумевается, когда @Column опущен. И всегда рекомендуется использовать @Enumerated, поскольку порядковый номер - это ужасная вещь по умолчанию. :)
Думаю, стоит упомянуть, что вам действительно нужна таблица person_interest
Мне пришлось добавить параметр joinColumn, чтобы он работал @CollectionTable(name = "person_interest", joinColumns = {@JoinColumn(name = "person_id")})
Я использую небольшую модификацию 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();
}
Преимущества:
java.util.EnumSet)Недостатки:
RegularEnumSet и PersistentEnumSet почти одинаковы)
EnumSet.noneOf(enumType) в свой PersistenEnumSet, объявить AccessType.PROPERTY и предоставить два метода доступа, которые используют отражение для чтения и записи поля elements.@Embeddable в PersistentEnumSet и отбросить
высший класс (... interests = new PersistentEnumSet<>(InterestsEnum.class);)@AttributeOverride, как указано в моем примере, если у вас более одного PersistentEnumSet в вашем объекте (в противном случае оба будут сохранены в одном столбце «элементы»).values() с отражением в конструкторе не оптимален (особенно если смотреть на производительность), но два других варианта также имеют свои недостатки:
EnumSet.getUniverse(), использует класс sun.misc.Я смог сделать это таким простым способом:
@ElementCollection(fetch = FetchType.EAGER)
Collection<InterestsEnum> interests;
Активная загрузка требуется, чтобы избежать ошибки инициализации ленивой загрузки, как объяснено в здесь.
tl; dr Краткое решение:
@ElementCollection(targetClass = InterestsEnum.class)
@CollectionTable
@Enumerated(EnumType.STRING)
Collection<InterestsEnum> interests;
Длинный ответ заключается в том, что с этими аннотациями JPA создаст одну таблицу, которая будет содержать список InterestsEnum, указывающий на идентификатор основного класса (в данном случае Person.class).
@ElementCollections указывает, где JPA может найти информацию о Enum
@CollectionTable создает таблицу, в которой хранятся отношения от Person до InterestsEnum.
@Enumerated (EnumType.STRING) сообщает JPA сохранять Enum как String, может быть EnumType.ORDINAL
В этом случае я не могу изменить эту коллекцию, потому что она сохраняется как неизменяемый PersistenceSet.
Моя ошибка. Мы можем изменить этот набор с помощью сеттера.
Это действительно только для JPA 1.0. В JPA 2.0 вы можете использовать аннотацию @ElementCollection, как показано выше.