Java 如何将枚举与JPA一起使用

Java 如何将枚举与JPA一起使用,java,jpa,enums,toplink,Java,Jpa,Enums,Toplink,我有一个电影租赁系统的现有数据库。每部电影都有一个等级属性。在SQL中,他们使用约束来限制此属性的允许值 CONSTRAINT film_rating_check CHECK ((((((((rating)::text = ''::text) OR ((rating)::text = 'G'::text)) OR ((rating)::text = 'PG'::text)) OR ((rating)::text = 'P

我有一个电影租赁系统的现有数据库。每部电影都有一个等级属性。在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--服务器会话(4729123)--异常[TOPLINK-116](Oracle) TopLink Essentials-2.0.1(构建b09d fcs(12/06/2007)): oracle.toplink.essentials.exceptions.DescriptorException异常 说明:中的值[NC-17]未提供转换值 现场[电影分级]。映射: oracle.toplink.essentials.mappings.DirectToFieldMapping[rating-->FILM.rating] 描述符:关系描述符(de.fhw.nsdb.entities.Film--> [数据库表(胶片)])

干杯


timo

我不知道toplink的内部结构,但我有根据地猜测如下:它使用Rating.valueOf(String s)方法映射到另一个方向。不可能重写valueOf(),因此必须坚持java的命名约定,以允许使用正确的valueOf方法

public enum Rating {

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

    public String getRating() {
        return name().replace("_","-");;
    }
}
getRating生成“人类可读”评级。请注意,枚举标识符中不允许使用“-”字符

当然,您必须将这些值存储在数据库中为NC_17。

这是怎么回事

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;
}
这样,您可以在数据库和实体上使用分级作为字符串,但在其他所有内容中都使用枚举。

public enum Rating{
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;
    }
}
未评级(“”), G(“G”), PG(“PG”), 第13页(“第13页”), R(“R”), NC17(“NC-17”); 私人字符串评级; 私有静态映射评级=新HashMap(); 静止的{ for(评级r:EnumSet.allOf(评级.class)){ ratings.put(r.toString(),r); } } 私有静态评级getRating(字符串评级){ 返回评级。获取(评级); } 私人评级(字符串评级){ 这个。评级=评级; } @凌驾 公共字符串toString(){ 回报率; } }

但是,我不知道如何在带有注释的TopLink端进行映射。

听起来您需要添加对自定义类型的支持:


这里有一个问题,那就是JPA在处理枚举时的能力有限。使用枚举,您有两种选择:

  • 将它们存储为一个等于
    Enum.ordinal()
    的数字,这是一个糟糕的想法(imho);或
  • 将它们存储为字符串,等于
    Enum.name()
    注意:不像您预期的那样
    toString()
    ,特别是因为
    Enum.toString()
    的默认行为是返回
    name()
  • 我个人认为最好的选择是(2)

    现在,您有一个问题,您定义的值在Java中不表示Valid实例名称(即使用连字符)。因此,您的选择是:

    • 更改您的数据
    • 持久化字符串字段,并隐式地将它们转换为对象中的枚举或从中转换;或
    • 使用诸如TypeConverter之类的非标准扩展
    我会按照优先顺序(从早到晚)做

    有人建议使用Oracle TopLink的转换器,但您可能正在使用TopLink Essentials,它是商业Oracle TopLink产品的一个子集,是JPA 1.0的参考实现


    作为另一个建议,我强烈建议改为。这是一个比Toplink Essentials更为完整的实现,Eclipselink将在发布时作为JPA 2.0的参考实现(预计于明年中由JavaOne发布)。

    您是否尝试存储序号值。如果没有与该值关联的字符串,则存储字符串值可以正常工作:

    @Enumerated(EnumType.ORDINAL)
    
    使用此注释

    @Column(columnDefinition="ENUM('User', 'Admin')")
    
    决心!!! 在那里我找到了答案:

    简单地说,转换查找的是enum的名称,而不是属性“rating”的值。 在您的情况下:如果在db值“NC-17”中有,则需要在枚举中有:

    枚举评级{
    (…)
    NC-17(“NC-17”);

    (…)

    问题是,我认为,JPA从来没有想到我们可能已经有了一个复杂的预先存在的模式

    我认为这导致了两个主要缺点,具体到Enum:

  • 使用name()和ordinal()的局限性。为什么不像使用@Entity那样用@Id标记getter呢
  • Enum通常在数据库中具有表示形式,允许与各种元数据关联,包括专有名称、描述性名称,可能还有本地化名称等。我们需要Enum的易用性与实体的灵活性相结合
  • 帮助我的事业并对Enum投票 公共枚举ParentalControlLevelsEnum{ U(“U”)、PG(“PG”)、12(“12”)、15(“15”)、18(“18”)

    }

    比较->枚举

    公共类RatingComparator实现了Comparator{

    public int compare(final ParentalControlLevelsEnum o1, final ParentalControlLevelsEnum o2) {
        if (o1.ordinal() < o2.ordinal()) {
            return -1;
        } else {
            return 1;
        }
    }
    
    public int compare(最终ParentalControlLevelsEnum o1,最终ParentalControlLevelsEnum o2){
    if(o1.ordinal()

    }

    使用您现有的
    枚举评级
    。您可以使用
    属性转换器
    s

    @转换器(autoApply=true)
    公共类RatingConverter实现AttributeConverter{
    @凌驾
    公共字符串convertToDatabaseColumn(额定值){
    如果(额定值==null){
    返回null;
    }
    返回评级。toString();
    }
    @凌驾
    公共评级convertToEntityAttribute(字符串代码){
    如果(代码==null){
    返回null;
    }
    回流(
    
    public int compare(final ParentalControlLevelsEnum o1, final ParentalControlLevelsEnum o2) {
        if (o1.ordinal() < o2.ordinal()) {
            return -1;
        } else {
            return 1;
        }
    }