Коллекция карт JPA Enums

Есть ли способ в JPA сопоставить коллекцию перечислений в классе Entity? Или единственное решение - обернуть Enum другим классом домена и использовать его для сопоставления коллекции?

@Entity
public class Person {
    public enum InterestsEnum {Books, Sport, etc...  }
    //@???
    Collection<InterestsEnum> interests;
}

Я использую реализацию Hibernate JPA, но, конечно, предпочел бы независимое от реализации решение.

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
99
0
72 417
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

Коллекции в JPA относятся к отношениям «один ко многим» или «многие ко многим» и могут содержать только другие сущности. Извините, но вам нужно обернуть эти перечисления в объект. Если подумать, вам все равно понадобится какое-то поле идентификатора и внешний ключ для хранения этой информации. Это если вы не сделаете что-то безумное, например, сохраните список, разделенный запятыми, в String (не делайте этого!).

Это действительно только для JPA 1.0. В JPA 2.0 вы можете использовать аннотацию @ElementCollection, как показано выше.

rustyx 27.12.2010 15:17
Ответ принят как подходящий

используя 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

Walter White 21.04.2010 20:59

Вы можете найти образец в ответе на этот вопрос: stackoverflow.com/q/3152787/363573

Stephan 14.08.2011 02:24

Поскольку вы упомянули Hibernate, я подумал, что этот ответ может зависеть от поставщика, но я не думаю, что это так, если использование JoinTable не вызывает проблем в других реализациях. Из того, что я видел, я считаю, что вместо этого следует использовать CollectionTable. Это то, что я использовал в своем ответе, и это работает для меня (хотя да, я также использую Hibernate прямо сейчас.)

spaaarky21 06.03.2012 00:51

Я знаю, что это старый поток, но мы реализуем то же самое, используя 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 выдают глупые ошибки при сборке.

LinuxLars 25.02.2015 17:32

Для чего это стоит - ошибки, которые я вижу, это баг - issues.jboss.org/browse/JBIDE-16016

LinuxLars 25.02.2015 17:59

Ссылка в ответе Энди - отличная отправная точка для сопоставления коллекций "не-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/…

DataNucleus 24.04.2013 13:52

Все, что вам действительно нужно, это @ElementCollection и Collection<InterestsEnum> interests;. Остальное потенциально полезно, но не нужно. Например, @Enumerated(EnumType.STRING) помещает в вашу базу данных удобочитаемые строки.

CorayThan 08.05.2013 21:08

Вы правы - в этом примере вы можете полагаться на подразумеваемый @Columnname. Я просто хотел прояснить, что подразумевается, когда @Column опущен. И всегда рекомендуется использовать @Enumerated, поскольку порядковый номер - это ужасная вещь по умолчанию. :)

spaaarky21 08.05.2013 23:34

Думаю, стоит упомянуть, что вам действительно нужна таблица person_interest

lukaszrys 24.10.2014 18:36

Мне пришлось добавить параметр joinColumn, чтобы он работал @CollectionTable(name = "person_interest", joinColumns = {@JoinColumn(name = "person_id")})

Tiago 31.08.2015 22:00

Я использую небольшую модификацию 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)
  • Набор - это всего лишь один числовой столбец в базе данных
  • все просто 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
  • Нелегко использовать поле elements в запросе критериев или JPQL
    • Вы можете использовать бинарные операторы или столбец битовой маски с соответствующими функциями, если ваша база данных поддерживает это.

Я смог сделать это таким простым способом:

@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.

Nicolazz92 25.08.2020 19:32

Моя ошибка. Мы можем изменить этот набор с помощью сеттера.

Nicolazz92 25.08.2020 19:58

Другие вопросы по теме