5.2.1.3. Атрибуты типа enum
В стандартном варианте использования 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
останется верной.
Тип поля Integer
удобно использовать в случаях, когда необходим упорядоченный список констант, подлежащий сортировке, например, в запросах JPQL и SQL (>
, <
, >=
, ⇐
, order
by
), кроме того, он имеет незначительное преимущество перед String
в плане производительности формата хранения и занимаемого места. С другой стороны, значения типа Integer
сами по себе неочевидны и могут затруднять отладку и интерпретацию результатов запросов, они неудобны в работе с голыми данными и сериализованными форматами. Если отношение упорядочения между константами не требуется, удобнее использовать тип String
.
Перечисления могут быть созданы в CUBA Studio на вкладке DATA MODEL (New → Enumeration). Чтобы использовать перечисление в качестве атрибута сущности, в редакторе атрибута нужно выбрать ENUM
в поле Attribute type и класс перечисления в поле Type. Значениям перечисления могут быть сопоставлены локализованные названия для отображения в пользовательском интерфейсе приложения.