4.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 останется верной.

Значениям перечисления могут быть сопоставлены локализованные названия для отображения в пользовательском интерфейсе приложения.