104

JPAにEntityクラス内の列挙型のコレクションをマップする方法はありますか?または、唯一の解決策は、列挙型を別のドメインクラスでラップし、それを使用してコレクションをマップすることですか?

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

私はHibernateJPA実装を使用していますが、もちろん実装に依存しないソリューションを好みます。

4

6 に答える 6

125

あなたができるHibernateを使用して

@CollectionOfElements(targetElement = InterestsEnum.class)
@JoinTable(name = "tblInterests", joinColumns = @JoinColumn(name = "personID"))
@Column(name = "interest", nullable = false)
@Enumerated(EnumType.STRING)
Collection<InterestsEnum> interests;
于 2009-03-13T13:50:47.250 に答える
71

Andy's answer のリンクは、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;
}
于 2012-02-27T01:24:29.357 に答える
8

tl;dr A short solution would be the following:

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

@ElementCollections specify where JPA can find information about the Enum

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

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

于 2020-04-18T00:41:18.090 に答える
5

私は 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してください) 。
  • セットは、データベース内の 1 つの数値列にすぎません
  • すべてがプレーンなJPAです(プロバイダー固有のカスタムタイプはありません)
  • 他のソリューションと比較して、同じタイプの新しいフィールドの簡単な (そして短い) 宣言

欠点:

  • コードの重複 (RegularEnumSetPersistentEnumSetはほぼ同じ)
    • EnumSet.noneOf(enumType)の結果をでラップし、リフレクションを使用してフィールドを読み書きする 2 つのアクセス メソッドをPersistenEnumSet宣言および提供できます。AccessType.PROPERTYelements
  • 永続セットに格納する必要がある列挙型クラスごとに、追加のセット クラスが必要です。
    • 永続化プロバイダーが public コンストラクターなしで埋め込み可能オブジェクトをサポートしている場合は、余分なクラス ( )を追加@Embeddableして削除できます。PersistentEnumSet... interests = new PersistentEnumSet<>(InterestsEnum.class);
  • @AttributeOverrideエンティティに複数ある場合は、私の例に示すように、を使用する必要がありPersistentEnumSetます (そうしないと、両方が同じ列「要素」に格納されます)。
  • コンストラクターでのリフレクションを使用したアクセスはvalues()最適ではありませんが (特にパフォーマンスを見る場合)、他の 2 つのオプションにも欠点があります。
    • クラスEnumSet.getUniverse()を利用するような実装sun.misc
    • 値の配列をパラメーターとして指定すると、指定された値が正しい値ではないというリスクがあります
  • 最大 64 個の値を持つ列挙型のみがサポートされています (これは本当に欠点ですか?)
    • 代わりに BigInteger を使用できます
  • クライテリア クエリや JPQL で elements フィールドを使用するのは簡単ではありません
    • データベースがサポートしている場合は、二項演算子またはビットマスク列を適切な関数で使用できます。
于 2015-01-27T22:27:11.570 に答える
0

JPAのコレクションは、1対多または多対多の関係を参照し、他のエンティティのみを含めることができます。申し訳ありませんが、これらの列挙型をエンティティでラップする必要があります。あなたがそれについて考えるならば、とにかくこの情報を保存するためにある種のIDフィールドと外部キーが必要になるでしょう。これは、コンマ区切りのリストを文字列に格納するなど、おかしなことをしない限りです(これは行わないでください)。

于 2009-01-06T11:52:27.460 に答える