列挙型のJPAマップコレクション


92

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

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

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

回答:


112

Hibernateを使用すると、次のことができます

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

141
誰かが今これを読んだ場合... @ CollectionOfElementsは非推奨になりました、代わりに次を使用してください:@ElementCollection

2
この質問の回答にサンプルがあります:stackoverflow.com/q/3152787/363573
Stephan

1
Hibernateについておっしゃっていたので、この回答はベンダー固有のものである可能性があると思いましたが、JoinTableを使用すると他の実装で問題が発生しない限り、そうではないと思います。私が見てきたことから、代わりにCollectionTableを使用する必要があると思います。それが私の答えで使用したものであり、それは私のために機能します(はい、私は現在Hibernateも使用しています)
spaaarky21 2012年

これが古いスレッドであることは知っていますが、javax.persistenceを使用して同じ種類のものを実装しています。追加する場合:@ElementCollection(targetClass = Roles.class)@CollectionTable(name = "USER_ROLES"、joinColumns = @ JoinColumn(name = "USER_ID"))@ Column(name = "ROLE"、nullable = false)@Enumerated( EnumType.STRING)プライベートSet <Roles>ロール; Userテーブルでは、モデルパッケージ全体で物事がはがれています。Userオブジェクトでは、主キーの@Id ... @ GeneratedValueジェネレーターと最初の@OneToManyのエラーでさえ、ビルド時に間抜けなエラーをスローします。
LinuxLars 2015

何それの価値について- -私が見ているエラーはバグですissues.jboss.org/browse/JBIDE-16016
LinuxLars

65

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

4
悲しいことに、一部の「管理者」は理由を説明せずにその回答を削除することを決定しました(ここのコースのパーについて)。参考までに、datanucleus.org / products / accessplatform_3_0 / jpa / orm /…
DataNucleus

2
これから実際に必要なのは@ElementCollectionCollection<InterestsEnum> interests; 残りは潜在的に有用ですが不要です。たとえば、@Enumerated(EnumType.STRING)人間が読める文字列をデータベースに配置します。
CorayThan 2013年

2
あなたは正しいです-この例では、あなたがに頼ることができる@Columnのがname暗示されています。@Columnが省略された場合の意味を明確にしたかっただけです。また、序数はデフォルトでひどいものであるため、@ Enumeratedを常にお勧めします。:)
spaaarky21 2013年

実際にperson_interestテーブルが必要であることを言及する価値があると思います
lukaszrys 2014年

それを機能させるには、joinColumnパラメーターを追加する必要がありました@CollectionTable(name="person_interest", joinColumns = {@JoinColumn(name="person_id")})
Tiago

8

私はこの簡単な方法でこれを達成することができました:

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

ここで説明するように、遅延読み込みの初期化エラーを回避するには、より積極的な読み込みが必要です。


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です(プロバイダー固有のカスタムタイプはありません)
  • 他のソリューションと比較して、同じタイプの新しいフィールドの簡単な(そして短い)宣言

欠点:

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

4

tl; dr簡単な解決策は次のとおりです。

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

長い答えは、このアノテーションを使用して、JPAはメインクラス識別子(この場合はPerson.class)を指すInterestsEnumのリストを保持する1つのテーブルを作成するということです。

@ElementCollectionsは、JPAが列挙型に関する情報を見つけることができる場所を指定します

@CollectionTableは、PersonからInterestsEnumへの関係を保持するテーブルを作成します

@Enumerated(EnumType.STRING)は、列挙型を文字列として永続化するようにJPAに指示します。EnumType.ORDINALの場合があります。


この場合、このコレクションは不変のPersistenceSetとして保存されるため、変更できません。
Nicolazz92

私の間違い。このセットはセッターで変更できます。
Nicolazz92

0

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


8
これはJPA1.0でのみ有効です。JPA 2.0では、上記のように@ElementCollectionアノテーションを使用できます。
rustyx 2010
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.