如何将枚举与 JPA 一起使用

2022-09-01 06:38: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() 方法,方向枚举 -> 字符串工作正常,但字符串 ->枚举不起作用。我得到以下异常:

[TopLink Warning]: 2008.12.09 01:30:57.434--ServerSession(4729123)--Exception [TOPLINK-116] (Oracle TopLink Essentials - 2.0.1 (Build b09d-fcs (12/06/2007)):oracle.toplink.essentials.exceptions.exceptions.DescriptorException Exception Description: 未为字段 [FILM.评级]。Mapping: oracle.toplink.essentials.mappings.DirectToFieldMapping[rating-->FILM.RATING] Descriptor: RelationalDescriptor(de.fhw.nsdb.entities.Film --> [DatabaseTable(FILM)])

干杯

蒂莫


答案 1

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

@Enumerated(EnumType.ORDINAL)

答案 2

您在这里遇到了一个问题,那就是JPA在处理枚举时的功能有限。对于枚举,您有两种选择:

  1. 将它们存储为等于的数字,这是一个可怕的想法(恕我直言);或Enum.ordinal()
  2. 将它们存储为等于 的字符串。注意:不是您所期望的,特别是因为默认行为是返回 。Enum.name()toString()Enum.toString()name()

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

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

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

我会按照这个顺序(从第一个到最后一个)作为优先顺序来做。

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

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