JPA карта коллекции Enums - PullRequest
77 голосов
/ 06 января 2009

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

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

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

Ответы [ 5 ]

101 голосов
/ 13 марта 2009

используя Hibernate вы можете сделать

@CollectionOfElements(targetElement = InterestsEnum.class)
@JoinTable(name = "tblInterests", joinColumns = @JoinColumn(name = "personID"))
@Column(name = "interest", nullable = false)
@Enumerated(EnumType.STRING)
Collection<InterestsEnum> interests;
58 голосов
/ 27 февраля 2012

Ссылка в ответе Энди является отличной отправной точкой для сопоставления коллекций объектов, не относящихся к сущности, в 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;
}
5 голосов
/ 23 марта 2017

Мне удалось сделать это простым способом:

@ElementCollection(fetch = FetchType.EAGER)
Collection<InterestsEnum> interests;

Требуется ускоренная загрузка, чтобы избежать ошибки инициализации при отложенной загрузке, как объяснено здесь .

4 голосов
/ 28 января 2015

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...