web-dev-qa-db-ja.com

JPA列挙型マッピング。最善のアプローチ

通常どおりJava列挙型には対応するコードと名前の説明があります。また、Javaこのようなフィールドを含むクラスは、Enumとしてそれらを含みます。

public enum MyEnum{
    SOMEINSTANCE(1, "test1"),
    SOMEINSTANCE(2, "test2");

    private final int code;
    private final String name;
    private MyEnum(int code, String name){
        this.code = code;
        this.name = name;
    }
    ... helper getter for code and name
}

@Entity
puclic class EnumHolder{
    private MyEnum myEnum;
}

私はJPAの初心者ですが、次のような 'myEnums'テーブルが必要です。

code int not null, name varchar(50) not null)

そして、私のenumHolderテーブルには、myEnumsテーブルを指すmyEnumCodeフィールドが必要です。

Currenltyを使用すると、EnumType.ORDINALとEnumType.STRINGの両方がサポートされます。良いアイデアではないと思います。

そして別の質問。 Java myEnumsクラスデータを使用してMyEnumテーブルに入力するにはどうすればよいですか?どうすればよいですか?最善のアプローチをお願いします。

PS:ここに私が提供できるソリューションがあります:

myEnumと名前codeを持つテーブルfieldsがあるとしましょう。 Java MyEnum enum、これは質問で説明されています。enumHolderテーブルにはmyEnumCode参照が必要ですmyEnum.codeフィールド。同意できない場合は、解決策についてコメントしてください。

@Entity
@Access(AccessType.FIELD)
public class EnumHolder {
    @Id private int id;
    @Transient private MyEnum myEnum;
    …
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    public MyEnum getMyEnum() { return MyEnum; }
    public void setMyEnum(MyEnum myEnum) { this.myEnum = myEnum; }

    @Access(AccessType.PROPERTY) @Column(name="myEnumCode")
    protected int getMyEnumForDb() {
        return myEnum.getCode();
    }

    protected void setMyEnumForDb(int enumCode) {
        myEnum = MyEnum.getByCode( enumCode);
    }
…
}

もちろん、ここには欠点があります。しかし、現時点では、より良いアプローチがわかりません。 EnumType.ORDINALおよびEnumType.STRINGを使用した代替は提供しないでください。私はここでその使用法に存在する可能性のあるすべての問題を書きたくはありません(Effective Javaで通常の使用法について説明されています)。EnumType.STRINGを使用してデータベースに説明を入れて、dbから要求することを許可しないでください。

Fillindデータベースについて。 myEnumテーブルをクリアし、それぞれのJava enum istanceがテーブルに挿入するスクリプトを作成することは難しくありません。そして、常に展開フェーズ中に実行します。 。

33
Alexandr

最善の方法は、一意のIDを各列挙型にマップし、ORDINALとSTRINGの落とし穴を回避することです。これを参照してください post これは、列挙型をマップできる5つの方法の概要を示しています。

上記のリンクから取得:

1&2。 @Enumeratedの使用

現在、@ Enumeratedアノテーションを使用して、JPAエンティティ内で列挙型をマッピングする方法は2つあります。残念ながら、EnumType.STRINGとEnumType.ORDINALの両方に制限があります。

EnumType.Stringを使用する場合、列挙型のいずれかの名前を変更すると、列挙値がデータベースに保存されている値と同期しなくなります。 EnumType.ORDINALを使用する場合、enum内の型を削除または並べ替えると、データベースに保存されている値が間違ったenum型にマップされます。

これらのオプションはどちらも脆弱です。データベースの移行を実行せずに列挙型を変更すると、データの整合性が損なわれる可能性があります。

3。ライフサイクルコールバック

考えられる解決策は、JPAライフサイクルコールバックアノテーション@PrePersistおよび@PostLoadを使用することです。エンティティに2つの変数があるため、これは非常にい感じがします。 1つはデータベースに格納された値をマッピングし、もう1つは実際の列挙をマッピングします。

4。一意のIDを各列挙型にマッピングする

推奨される解決策は、列挙内で定義された固定値またはIDに列挙をマップすることです。定義済みの固定値にマッピングすると、コードがより堅牢になります。列挙型の順序の変更、または名前のリファクタリングは、悪影響を引き起こしません。

5。 Java EE7 @Convertを使用

JPA 2.1を使用している場合、新しい@Convertアノテーションを使用するオプションがあります。これには、@ Converterアノテーションが付けられたコンバータークラスを作成する必要があります。このクラスでは、列挙型ごとにデータベースに保存する値を定義します。エンティティ内で、enumに@Convertアノテーションを付けます。

私の好み:(数4)

コンバーターの使用とは反対に列挙内でIDを定義することを好む理由は、適切なカプセル化です。列挙型のみがそのIDを知る必要があり、エンティティのみが列挙型をデータベースにマップする方法を知る必要があります。

コード例については、元の post を参照してください。

49
Chris Ritchie

@Enumerated を使用して、列挙フィールドをエンティティに追加できます。ここでのキッカーは、ケーキを食べて食べたいということです-一貫性のために、EnumType.ORDINALまたはEnumType.STRINGを選択する方が良いでしょう。

両方ともプラスとマイナスがあり、これらは このサイトで にリストされています。大きな違いは順序付けにあるようです-EnumType.ORDINALセットがある場合、enumの更新時に問題が発生します。

テーブルのデザイン方法を再考することをお勧めします。文字列と整数は本質的に同じ情報を格納します-列挙値が何であるか-そしてあなたはそれをデータベースからあまり手間をかけずに取得できます。テーブルの主キーとしてidを使用し、上記のリンクの注意事項を考慮して、列挙型を文字列または序数値のいずれかにするとよいでしょう。

12
Makoto
@Column(name = "MY_TYPE")
@Enumerated(EnumType.STRING)
private MyType type;

enum MyType {
type_1
type_2
}

次に、次のような列を作成します。

TYPE_ID  VARCHAR(20) NOT NULL DEFAULT 'TYPE_1' CHECK (TYPE_ID IN ('TYPE_1', 'TYPE_2')))

これで問題が解決しました。

2
Monsif Mabrouk