35

映画レンタル システムの既存のデータベースがあります。各映画には評価属性があります。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 列挙型を使用するとよいと思います。ただし、"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() メソッドを使用すると、方向 enum -> String は正常に機能しますが、String -> enum は機能しません。次の例外が発生します。

[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)])

乾杯

ティモ

4

11 に答える 11

31

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

@Enumerated(EnumType.ORDINAL)
于 2009-01-08T20:44:12.987 に答える
25

ここで問題が発生します。これは、列挙型の処理に関してJPAの機能が制限されていることです。列挙型では、2つの選択肢があります。

  1. それらを等しい数としてEnum.ordinal()保存します。これはひどい考えです(imho)。また
  2. それらをに等しい文字列として格納しますEnum.name()注:特にデフォルトの動作はを返すことtoString()であるため、予想どおりではありません。Enum.toString()name()

個人的には(2)が一番いいと思います。

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

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

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

誰かがOracleTopLinkのコンバータを提案しましたが、おそらくToplink Essentialsを使用しています。これは、商用のOracleToplink製品のサブセットであるリファレンスJPA1.0実装です。

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

于 2008-12-23T06:30:15.930 に答える
8

カスタムタイプのサポートを追加する必要があるようです:

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

于 2008-12-09T15:05:57.287 に答える
5
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 側でマッピングを行う方法がわかりません。

于 2008-12-09T14:29:11.900 に答える
2

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 は、「人間が読める」評価を生成します。「-」文字は、列挙型識別子では許可されていないことに注意してください。

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

于 2008-12-09T13:18:43.070 に答える
1

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

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

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

JPA_SPEC-47に投票して、私の大義を支援してください

于 2013-06-23T18:30:41.573 に答える
0

これはどうですか

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 とエンティティの両方で評価を文字列として使用しますが、他のすべてには列挙型を使用します。

于 2008-12-09T14:05:14.257 に答える
-1

この注釈を使用

@Column(columnDefinition="ENUM('User', 'Admin')")
于 2011-10-04T08:29:25.110 に答える
-2

解決しました!!! 答えを見つけた場所: http://programming.itags.org/development-tools/65254/

簡単に言えば、変換は属性「rating」の値ではなく、列挙型の名前を探します。あなたの場合:データベース値「NC-17」にある場合は、列挙型に含める必要があります:

列挙評価 {
(...)
NC-17 ( "NC-17" );
(...)

于 2012-08-07T14:32:05.843 に答える