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 在处理枚举方面的能力有限。使用枚举,您有两种选择:

  1. 将它们存储为等于 的数字Enum.ordinal(),这是一个糟糕的主意(恕我直言);或者
  2. 将它们存储为等于 的字符串Enum.name()注意:不像toString()你想象的那样,特别是因为默认行为Enum.toString()是返回name()

我个人认为最好的选择是(2)。

现在您遇到了一个问题,即您定义的值不代表 Java 中的有效实例名称(即使用连字符)。所以你的选择是:

  • 更改您的数据;
  • 持久化字符串字段并隐式将它们转换为对象中的枚举或从枚举转换;或者
  • 使用非标准扩展,例如 TypeConverters。

我会按照优先顺序(从第一个到最后一个)执行它们。

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

作为另一个建议,我强烈建议切换到EclipseLink。它是一个比 Toplink Essentials 更完整的实现,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(),因此您必须遵守 java 的命名约定,以允许正确的 valueOf 方法。

public enum Rating {

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

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

getRating 产生“人类可读”的评级。请注意,枚举标识符中不允许使用“-”字符。

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

于 2008-12-09T13:18:43.070 回答
1

问题是,我认为,JPA 从来没有考虑到我们可以拥有一个复杂的预先存在的模式。

我认为这导致了 Enum 特有的两个主要缺点:

  1. 使用 name() 和 ordinal() 的限制。为什么不像我们使用@Entity 那样用@Id 标记getter?
  2. 枚举通常在数据库中有表示,以允许与各种元数据关联,包括专有名称、描述性名称,可能还有本地化等。我们需要枚举的易用性和实体的灵活性。

帮助我的事业并为JPA_SPEC-47投票

于 2013-06-23T18:30:41.573 回答
1

使用您现有的enum Rating. 您可以使用AttributeCoverters。

@Converter(autoApply = true)
public class RatingConverter implements AttributeConverter<Rating, String> {

    @Override
    public String convertToDatabaseColumn(Rating rating) {
        if (rating == null) {
            return null;
        }
        return rating.toString();
    }

    @Override
    public Rating convertToEntityAttribute(String code) {
        if (code == null) {
            return null;
        }

        return Stream.of(Rating.values())
          .filter(c -> c.toString().equals(code))
          .findFirst()
          .orElseThrow(IllegalArgumentException::new);
    }
}
于 2020-04-08T00:55:58.877 回答
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;
}

有了这个,您在数据库和实体上都使用评级作为字符串,但将枚举用于其他所有内容。

于 2008-12-09T14:05:14.257 回答
-1

使用这个注解

@Column(columnDefinition="ENUM('User', 'Admin')")
于 2011-10-04T08:29:25.110 回答
-1

枚举公共枚举 ParentalControlLevelsEnum { U("U"), PG("PG"), _12("12"), _15("15"), _18("18");

private final String value;

ParentalControlLevelsEnum(final String value) {
    this.value = value;
}

public String getValue() {
    return value;
}

public static ParentalControlLevelsEnum fromString(final String value) {
    for (ParentalControlLevelsEnum level : ParentalControlLevelsEnum.values()) {
        if (level.getValue().equalsIgnoreCase(value)) {
            return level;
        }
    }
    return null;
}

}

比较 -> 枚举

公共类 RatingComparator 实现 Comparator {

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

}

于 2017-11-14T16:53:01.983 回答
-2

解决!!!我在哪里找到答案: http: //programming.itags.org/development-tools/65254/

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

枚举等级 {
(...)
NC-17 ("NC-17" );
(...)

于 2012-08-07T14:32:05.843 回答