В стандартном варианте использования JPA для атрибутов типа enum
в базе данных хранится целое число, получаемое методом ordinal()
этого перечисления. Такой подход может привести к следующим проблемам при эксплуатации и развитии системы:
-
при появлении в БД значения, не равного ни одному
ordinal
значению перечисления, экземпляр сущности нельзя загрузить вообще; -
невозможно ввести новое значение между имеющимися, что актуально при использовании сортировки по значению перечисления (order by).
Чтобы решить эти проблемы, в подходе 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
, задаваемое полем id
перечисления CustomerGrade
, а конкретно 10
, 20
или 30
. В то же время прикладной код и метаданные работают с самим типом CustomerGrade
через методы доступа, которые и осуществляют конвертацию.
При наличии в поле БД значения, не соответствующего ни одному значению перечисления, метод getGrade()
просто вернет null
. Для ввода нового значения, например, HIGHER
, между HIGH
и PREMIUM
, достаточно добавить это значение в перечисление с идентификатором 15
, при этом сортировка по полю Customer.grade
останется верной.
Значениям перечисления могут быть сопоставлены локализованные названия для отображения в пользовательском интерфейсе приложения.