枚举的JPA地图集

时间:2009-01-06 11:40:19

标签: java jpa java-ee

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

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

我正在使用Hibernate JPA实现,但当然更喜欢与实现无关的解决方案。

6 个答案:

答案 0 :(得分:101)

使用Hibernate你可以做到

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

答案 1 :(得分:58)

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;
}

答案 2 :(得分:5)

我能够以这种简单的方式实现这一目标:

  win.loadURL('file://' + __dirname + '/index.html');

需要急切加载,以避免延迟加载inizializing错误,如here所述。

答案 3 :(得分:4)

我使用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(没有提供商特定的自定义类型
  • 与其他解决方案相比,轻松(和简短)声明相同类型的新字段

缺点:

  • 代码重复(RegularEnumSetPersistentEnumSet几乎相同)
    • 您可以将EnumSet.noneOf(enumType)的结果包装在PersistenEnumSet中,声明AccessType.PROPERTY并提供两种使用反射来读取和写入elements字段的访问方法
  • 每个应该存储在持久集中的枚举类需要一个额外的集合类
    • 如果您的持久性提供程序支持没有公共构造函数的嵌入式代码,则可以将@Embeddable添加到PersistentEnumSet并删除 额外课程(... interests = new PersistentEnumSet<>(InterestsEnum.class);
  • 如果您的实体中有多个@AttributeOverride,则必须使用我的示例中给出的PersistentEnumSet(否则两者都会存储到同一列中#34} ;元件&#34)
  • 在构造函数中使用反射访问values()并不是最佳的(特别是在查看性能时),但另外两个选项也存在缺陷:
    • EnumSet.getUniverse()这样的实现使用sun.misc
    • 将values数组作为参数提供存在给定值不正确的风险
  • 仅支持最多64个值的枚举(这真的是一个缺点吗?)
    • 您可以使用BigInteger
  • 在条件查询或JPQL中使用元素字段并不容易
    • 如果您的数据库支持
    • ,您可以使用带有相应函数的二元运算符或位掩码列

答案 4 :(得分:4)

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

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

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

@ElementCollections指定JPA在哪里可以找到有关枚举的信息

@CollectionTable创建一个表,该表保存从Person到InterestsEnum的关系

@Enumerated(EnumType.STRING)告诉JPA将Enum保留为String,可以是EnumType.ORDINAL

答案 5 :(得分:0)

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