如何在JPA中使用枚举

时间:2008-12-09 12:35:01

标签: 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 = '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()方法方向枚举 - >字符串工作正常,但String - >枚举不起作用。我得到以下异常:

  

[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异常   说明:没有为[NC-17]中的值提供转换值   字段[FILM.RATING]。制图:   oracle.toplink.essentials.mappings.DirectToFieldMapping [评级 - > FILM.RATING]   描述符:RelationalDescriptor(de.fhw.nsdb.entities.Film - >   [DatabaseTable(FILM)])

欢呼

蒂莫

11 个答案:

答案 0 :(得分:28)

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

@Enumerated(EnumType.ORDINAL)

答案 1 :(得分:24)

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

  1. 将它们存储为等于Enum.ordinal()的数字,这是一个糟糕的主意(imho);或
  2. 将它们存储为等于Enum.name()的字符串。 注意:不是您所期望的toString(),尤其是因为Enum.toString()的默认行为是返回name()
  3. 我个人认为最好的选择是(2)。

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

    • 更改您的数据;
    • 保留字符串字段并隐式将它们转换为对象中的枚举;或
    • 使用TypeConverters等非标准扩展程序。

    我会按顺序(从头到尾)按顺序排列。

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

    作为另一个建议,我强烈建议切换到EclipseLink。这是一个比Toplink Essentials更完整的实现,Eclipselink将成为JPA 2.0的参考实现(预计将于明年年中推出)。

答案 2 :(得分:7)

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

Extending OracleAS TopLink to Support Custom Type Conversions

答案 3 :(得分: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方面做映射。

答案 4 :(得分: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。

答案 5 :(得分:1)

我认为问题在于,JPA从未接受过这样的想法,即我们可能已经有一个复杂的预先存在的Schema。

我认为这有两个主要缺点,特别是Enum:

  1. 使用name()和ordinal()的限制。为什么不用@Id标记一个getter,就像我们用@Entity做的那样?
  2. Enum通常在数据库中表示允许与各种元数据相关联,包括正确的名称,描述性名称,可能具有本地化的东西等。我们需要易于使用Enum并结合实体的灵活性
  3. 帮助我的事业并在JPA_SPEC-47

    上投票

答案 6 :(得分: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;
}

这样你就可以在数据库和实体上使用等级作为字符串,但是将枚举用于其他所有内容。

答案 7 :(得分:0)

使用现有的enum Rating。您可以使用AttributeCoverter

@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);
    }
}

答案 8 :(得分:-1)

使用此注释

@Column(columnDefinition="ENUM('User', 'Admin')")

答案 9 :(得分:-1)

枚举 public enum 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;
}

}

比较 - &gt;枚举

公共类RatingComparator实现Comparator {

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

}

答案 10 :(得分:-2)

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

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

枚举评分{
(...)
NC-17 (“NC-17”);
(...)