質問

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

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

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

役に立ちましたか?

解決

Hibernateを使用してできること

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

他のヒント

アンディの答えのリンクは、<!> quot; non-Entity <!> quot;のコレクションをマッピングするための素晴らしい出発点です。 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;
}

この簡単な方法でこれを達成できました。

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

こちら

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

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

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top