3.2.1.3. 枚举属性
JPA 对 enum
属性的标准用法是使用数据库的整型字段,保存从 ordinal()
方法获得的值。在生产环境下对系统进行扩展时,这种方法可能会导致以下问题:
-
如果数据库中枚举的值不等于任何
ordinal
值,则无法加载实体实例。 -
不能在现有的值之间添加新的枚举值,但是这在需要按枚举值排序时很重要。
CUBA 中解决这些问题的方式是将存储在数据库中的值与枚举的 ordinal
值分离。要到这一点,实体的字段应该用存储在数据库中的字段类型声明(Integer
或 String
型),而实体的访问方法(getter / setter)则使用实际的枚举类型来定义。
例如:
@Entity(name = "sales$Customer")
@Table(name = "SALES_CUSTOMER")
public class Customer extends StandardEntity {
@Column(name = "GRADE")
protected Integer grade;
public CustomerGrade getGrade() {
return grade == null ? null : CustomerGrade.fromId(grade);
}
public void setGrade(CustomerGrade grade) {
this.grade = grade == null ? null : grade.getId();
}
...
}
在这种情况下,枚举类可以如下所示:
public enum CustomerGrade implements EnumClass<Integer> {
PREMIUM(10),
HIGH(20),
MEDIUM(30);
private Integer id;
CustomerGrade(Integer id) {
this.id = id;
}
@Override
public Integer getId() {
return id;
}
public static CustomerGrade fromId(Integer id) {
for (CustomerGrade grade : CustomerGrade.values()) {
if (grade.getId().equals(id))
return grade;
}
return null;
}
}
为了将枚举属性正常地反映在元数据中,枚举类必须实现 EnumClass
接口。
如示例所示,grade
属性对应于存储在数据库中的 Integer
类型值,该值由 CustomerGrade
枚举的 id
字段指定,即 10
、 20
或 30
。同时,应用程序代码和元数据框架通过访问方法(getter/setter)使用 CustomerGrade
枚举,这些方法中执行类型的转换。
如果数据库中的值没有对应的枚举值,这时 getGrade()
方法将只返回 null
。如果要添加一个新枚举值,例如 HIGHER
在 HIGH
和 PREMIUM
之间,只需添加 id = 15
的新枚举值就可以了,这样可以确保按 Customer.grade
字段正确排序。
Integer
字段类型可以提供有序的常量列表,并允许在 JPQL 和 SQL 查询中排序( >
、 <
、 >=
、 ⇐
、 order by
),同时也基本没有存储空间和性能方面的问题。但另一方面,在查询结果中,Integer
值不是“自描述”的,这使得在对数据库的原始数据或序列化后的数据进行调试时变地复杂了。就这点而言,使用 String
类型更方便。
可以在 CUBA Studio 中使用 Data Model > New > Enumeration 菜单创建枚举。要将枚举用作实体属性,请在属性编辑器的 Attribute type 字段中选择 ENUM
,然后在 Type 字段中选择枚举类。枚举值可以与在应用程序界面中显示的本地化名称相关联。