Java 枚举的 JPA 地图集合

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/416208/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 14:22:51  来源:igfitidea点击:

JPA map collection of Enums

javajpajakarta-ee

提问by Gennady Shumakher

Is there a way in JPA to map a collection of Enums within the Entity class? Or the only solution is to wrap Enum with another domain class and use it to map the collection?

JPA 中有没有办法映射实体类中的枚举集合?或者唯一的解决方案是用另一个域类包装 Enum 并使用它来映射集合?

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

I am using Hibernate JPA implementation, but of course would prefer implementation agnostic solution.

我正在使用 Hibernate JPA 实现,但当然更喜欢实现不可知的解决方案。

采纳答案by cletus

using Hibernate you can do

使用 Hibernate 你可以做

@CollectionOfElements(targetElement = InterestsEnum.class)
@JoinTable(name = "tblInterests", joinColumns = @JoinColumn(name = "personID"))
@Column(name = "interest", nullable = false)
@Enumerated(EnumType.STRING)
Collection<InterestsEnum> interests;

回答by cletus

Collections in JPA refer to one-to-many or many-to-many relationships and they can only contain other entities. Sorry, but you'd need to wrap those enums in an entity. If you think about it, you'd need some sort of ID field and foreign key to store this information anyway. That is unless you do something crazy like store a comma-separated list in a String (don't do this!).

JPA 中的集合是指一对多或多对多的关系,它们只能包含其他实体。抱歉,您需要将这些枚举包装在一个实体中。如果您考虑一下,无论如何您都需要某种 ID 字段和外键来存储此信息。除非你做一些疯狂的事情,比如在字符串中存储一个逗号分隔的列表(不要这样做!)。

回答by spaaarky21

The link in Andy's answer is a great starting point for mapping collections of "non-Entity" objects in JPA 2, but isn't quite complete when it comes to mapping enums. Here is what I came up with instead.

Andy 的回答中的链接是在 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;
}

回答by Tobias Liefke

I'm using a slight modification of java.util.RegularEnumSet to have a persistent EnumSet:

我正在对 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
  // ...
}

This class is now the base for all of my enum sets:

这个类现在是我所有枚举集的基础:

@Embeddable
public class InterestsSet extends PersistentEnumSet<InterestsEnum> {
  public InterestsSet() {
    super(InterestsEnum.class);
  }
}

And that set I can use in my entity:

我可以在我的实体中使用该集合:

@Entity
public class MyEntity {
  // ...
  @Embedded
  @AttributeOverride(name="elements", column=@Column(name="interests"))
  private InterestsSet interests = new InterestsSet();
}

Advantages:

好处:

  • Working with a type safe and performant enum set in your code (see java.util.EnumSetfor a description)
  • The set is just one numeric column in the database
  • everything is plain JPA (no provider specific custom types)
  • easy (and short) declaration of new fields of the same type, compared with the other solutions
  • 使用代码中设置的类型安全和高性能枚举(请参阅java.util.EnumSet有关说明)
  • 该集合只是数据库中的一个数字列
  • 一切都是普通的 JPA(没有提供者特定的自定义类型
  • 与其他解决方案相比,易于(且简短)声明相同类型的新字段

Drawbacks:

缺点:

  • Code duplication (RegularEnumSetand PersistentEnumSetare nearly the same)
    • You could wrap the result of EnumSet.noneOf(enumType)in your PersistenEnumSet, declare AccessType.PROPERTYand provide two access methods which use reflection to read and write the elementsfield
  • An additional set class is needed for every enum class that should be stored in a persistent set
    • If your persistence provider supports embeddables without a public constructor, you could add @Embeddableto PersistentEnumSetand drop the extra class (... interests = new PersistentEnumSet<>(InterestsEnum.class);)
  • You must use an @AttributeOverride, as given in my example, if you've got more than one PersistentEnumSetin your entity (otherwise both would be stored to the same column "elements")
  • The access of values()with reflection in the constructor is not optimal (especially when looking at the performance), but the two other options have their drawbacks as well:
    • An implementation like EnumSet.getUniverse()makes use of a sun.miscclass
    • Providing the values array as parameter has the risk that the given values are not the correct ones
  • Only enums with up to 64 values are supported (is that really a drawback?)
    • You could use BigInteger instead
  • It's not easy to use the elements field in a criteria query or JPQL
    • You could use binary operators or a bitmask column with the appropriate functions, if your database supports that
  • 代码重复(RegularEnumSet并且PersistentEnumSet几乎相同)
    • 您可以将 的结果包装EnumSet.noneOf(enumType)在您的PersistenEnumSet,声明AccessType.PROPERTY并提供两种使用反射来读取和写入elements字段的访问方法
  • 每个应该存储在持久集合中的枚举类都需要一个额外的集合类
    • 如果你的持久提供商支持embeddables没有公共构造函数,你可以添加@EmbeddablePersistentEnumSet并删除多余的类(... interests = new PersistentEnumSet<>(InterestsEnum.class);
  • @AttributeOverride如果您PersistentEnumSet的实体中有多个,则必须使用,如我的示例中所示(否则两者都将存储到同一列“元素”中)
  • values()在构造函数中访问with 反射不是最佳的(尤其是在查看性能时),但其他两个选项也有其缺点:
    • 类似的EnumSet.getUniverse()实现使用了一个sun.misc
    • 提供 values 数组作为参数有给定值不是正确值的风险
  • 仅支持最多 64 个值的枚举(这真的是一个缺点吗?)
    • 你可以改用 BigInteger
  • 在标准查询或 JPQL 中使用元素字段并不容易
    • 如果您的数据库支持,您可以使用具有适当功能的二元运算符或位掩码列

回答by megalucio

I was able to accomplish this in this simple way:

我能够以这种简单的方式完成此操作:

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

Eager loading is required in order to avoid lazy loading inizializing error as explained here.

预先加载需要,以避免延迟加载inizializing错误的解释在这里

回答by mizerablebr

tl;dr A short solution would be the following:

tl;dr 一个简短的解决方案如下:

@ElementCollection(targetClass = InterestsEnum.class)
@CollectionTable
@Enumerated(EnumType.STRING)
Collection<InterestsEnum> interests;

The long answer is that with this annotations JPA will create one table that will hold the list of InterestsEnum pointing to the main class identifier (Person.class in this case).

长的答案是,使用此注释,JPA 将创建一个表,该表将保存指向主类标识符(在本例中为 Person.class)的 InterestsEnum 列表。

@ElementCollections specify where JPA can find information about the Enum

@ElementCollections 指定 JPA 可以在何处找到有关 Enum 的信息

@CollectionTable create the table that hold relationship from Person to InterestsEnum

@CollectionTable 创建保存从 Person 到 InterestsEnum 的关系的表

@Enumerated(EnumType.STRING) tell JPA to persist the Enum as String, could be EnumType.ORDINAL

@Enumerated(EnumType.STRING) 告诉 JPA 将枚举作为字符串持久化,可以是 EnumType.ORDINAL