web-dev-qa-db-ja.com

JPAで列挙型を使用する方法

映画レンタルシステムの既存のデータベースがあります。各映画には評価属性があります。 SQLでは、制約を使用して、この属性の許容値を制限していました。

CONSTRAINT film_rating_check CHECK 
    ((((((((rating)::text = ''::text) OR 
          ((rating)::text = 'G'::text)) OR 
          ((rating)::text = 'PG'::text)) OR 
          ((rating)::text = 'PG-13'::text)) OR 
          ((rating)::text = 'R'::text)) OR 
          ((rating)::text = 'NC-17'::text)))

Java enumを使用して制約をオブジェクトの世界にマッピングするのは良いことだと思います。しかし、「PG-13」の特殊な文字のために、許可された値を単純に取得することはできませんそして「NC-17」ですので、次の列挙型を実装しました。

public enum Rating {

    UNRATED ( "" ),
    G ( "G" ), 
    PG ( "PG" ),
    PG13 ( "PG-13" ),
    R ( "R" ),
    NC17 ( "NC-17" );

    private String rating;

    private Rating(String rating) {
        this.rating = rating;
    }

    @Override
    public String toString() {
        return rating;
    }
}

@Entity
public class Film {
    ..
    @Enumerated(EnumType.STRING)
    private Rating rating;
    ..

ToString()メソッドを使用すると、方向の列挙->文字列は正常に機能しますが、文字列->列挙は機能しません。次の例外が発生します。

[TopLink警告]:2008.12.09 01:30:57.434--ServerSession(4729123)-例外[TOPLINK-116](Oracle TopLink Essentials-2.0.1(Build b09d-fcs(12/06/2007))): Oracle.toplink.essentials.exceptions.DescriptorException例外説明:フィールド[FILM.RATING]の値[NC-17]に変換値が指定されていません。マッピング:Oracle.toplink.essentials.mappings.DirectToFieldMapping [rating-> FILM.RATING]記述子:RelationalDescriptor(de.fhw.nsdb.entities.Film-> [DatabaseTable(FILM)])

乾杯

ティモ

35
Timo

カスタムタイプのサポートを追加する必要があるように聞こえます:

カスタム型変換をサポートするためのOracleAS TopLinkの拡張

8
Dan Vinton

順序値を保存しようとしましたか。値に文字列が関連付けられていない場合は、文字列値の保存が正常に機能します。

@Enumerated(EnumType.ORDINAL)
30
aledbf

ここに問題があり、それは列挙の処理に関してはJPAの限られた機能です。列挙型には、2つの選択肢があります。

  1. Enum.ordinal()に等しい数として保存します。これはひどい考えです(imho)。または
  2. Enum.name()と等しい文字列として保存します。 注:toString()ではなく、特にEnum.toString()のデフォルトの動作はname()

個人的には、最良の選択肢は(2)だと思います。

ここで、Java(つまりハイフンを使用))で有効なインスタンス名を表さない値を定義しているという問題があります。したがって、選択肢は次のとおりです。

  • データを変更します。
  • 文字列フィールドを永続化し、オブジェクトの列挙との間で暗黙的に変換します。または
  • TypeConvertersのような非標準の拡張機能を使用します。

優先順位として、その順序(最初から最後)でそれらを実行します。

誰かがOracle TopLinkのコンバータを提案しましたが、おそらく市販のOracle Toplink製品のサブセットである参照JPA 1.0実装であるToplink Essentialsを使用しているでしょう。

別の提案として、 EclipseLink に切り替えることを強くお勧めします。 Toplink Essentialsよりもはるかに完全な実装であり、Eclipselinkは、リリース時にJPA 2.0のリファレンス実装になります(JavaOneは来年半ばに予定)。

25
cletus
public enum Rating {

    UNRATED ( "" ),
    G ( "G" ), 
    PG ( "PG" ),
    PG13 ( "PG-13" ),
    R ( "R" ),
    NC17 ( "NC-17" );

    private String rating;

    private static Map<String, Rating> ratings = new HashMap<String, Rating>();
    static {
        for (Rating r : EnumSet.allOf(Rating.class)) {
            ratings.put(r.toString(), r);
        }
    }

    private static Rating getRating(String rating) {
        return ratings.get(rating);
    }

    private Rating(String rating) {
        this.rating = rating;
    }

    @Override
    public String toString() {
        return rating;
    }
}

ただし、注釈付きのTopLink側でマッピングを行う方法がわかりません。

5
JeeBee

私はtoplinkの内部を知りませんが、私が学んだ推測は次のとおりです。Rating.valueOf(String s)メソッドを使用して他の方向にマッピングします。 valueOf()をオーバーライドすることはできないため、正しいvalueOfメソッドを許可するには、Javaの命名規則に従う必要があります。

public enum Rating {

    UNRATED,
    G, 
    PG,
    PG_13 ,
    R ,
    NC_17 ;

    public String getRating() {
        return name().replace("_","-");;
    }
}

getRatingは、「人間が読み取れる」評価を生成します。 "-" chanracterは列挙型識別子では許可されないことに注意してください。

もちろん、NC_17としてDBに値を保存する必要があります。

2

問題は、複雑な既存のスキーマをすでに配置できるという考えを念頭に置いて、JPAが決して受け入れられなかったことだと思います。

これにより、Enumに固有の2つの主な欠点があると思います。

  1. Name()およびordinal()の使用の制限。なぜ@Entityで行うように、@ Idでゲッターをマークしないのですか?
  2. 通常、列挙型は、適切な名前、説明的な名前、ローカライズされた名前など、あらゆる種類のメタデータとの関連付けを可能にするデータベース内の表現を持っています。エンティティの柔軟性と組み合わせた列挙型の使いやすさが必要です。

私の原因を助けて、 JPA_SPEC-47 に投票してください

1
YoYo

これはどうですか

public String getRating{  
   return rating.toString();
}

pubic void setRating(String rating){  
   //parse rating string to rating enum
   //JPA will use this getter to set the values when getting data from DB   
}  

@Transient  
public Rating getRatingValue(){  
   return rating;
}

@Transient  
public Rating setRatingValue(Rating rating){  
   this.rating = rating;
}

これにより、DBとエンティティの両方で文字列として評価を使用しますが、他のすべてには列挙型を使用します。

0
Mg.