1. Опис

У попередній лекції ми говорили, що Hibernate використовує спеціальну колонку DTYPE VARCHAR для зберігання імені Entity-класу. Така колонка називається дискримінатор. Її використовують для того, щоб однозначно визначити, який клас потрібно створити для даного рядка бази даних.

Ти можеш керувати цією колонкою за допомогою анотації @DiscriminatorColumn. Приклад:


@DiscriminatorColumn(name="ім'я_колонки", discriminatorType = DiscriminatorType.INTEGER)

Відповідно до специфікації JPA дискримінатор може мати такі типи:

  • STRING
  • CHAR
  • INTEGER

Але Hibernate дозволяє трохи розширити цей список. Він підтримує такі Java-типи: String, char, int, byte, short, boolean.

Якщо ми використовуємо тип INTEGER, то як кодувати ім'я Entity-класу? Для цього використовується ще одна анотація — @DiscriminatorValue.

Подивися приклади:


@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="user_type", discriminatorType = DiscriminatorType.INTEGER)
@Entity
class User {
  int id;
  String name;
  LocalDate birthday;
}

@Entity
@DiscriminatorValue("1")
class Employee extends User {
 String occupation;
 int salary;
 LocalDate join;
}

@Entity
@DiscriminatorValue("2")
class Client extends User {
   String address;
}

У прикладі вище ми вказали Hibernate, що для дискримінатора використовуватиметься колонка user_type, яка зберігатиме числа. Якщо у ній зберігається значення 1, це означає, що тип рядка — Employee, якщо зберігається 2, то тип рядка — Client. Просто та красиво.

2. @DiscriminatorValue

Але це ще не все. Ти можеш вказати Hibernate, як інтерпретувати тип рядка, коли його дискримінатор дорівнює NULL.

Насправді це дуже просто. Ти вказуєш значення null в анотації @DiscriminatorValue. Наприклад, так:


@DiscriminatorValue("null")

Приклад класу:


@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="user_type", discriminatorType = DiscriminatorType.INTEGER)
@DiscriminatorValue("null")
@Entity
class User {
  int id;
  String name;
  LocalDate birthday;
}

Ми вказали Hibernate, що будь-який рядок таблиці, де в колонці user_type стоїть NULL, має бути інтерпретований як об'єкт типу User.

Але це ще не все. Є ще одне цікаве значення в анотації @DiscriminatorValue.

Ось таке:


@DiscriminatorValue("not null")

Приклад класу:


@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="user_type", discriminatorType = DiscriminatorType.INTEGER)
@DiscriminatorValue("not null")
@Entity
class User {
  int id;
  String name;
  LocalDate birthday;
}

За допомогою цієї анотації ми вказали Hibernate, що будь-який рядок таблиці, де в колонці user_type стоїть значення не NULL, має бути інтерпретована як об'єкт типу User. Але це тільки для випадку, якщо не буде знайдено клас, який має явно вказаний потрібний номер.

Так це працюватиме для різних значень дискримінаторів:

  • 0 — створюємо об'єкт типу User
  • 1 — створюємо об'єкт типу Employee
  • 2 — створюємо об'єкт типу Client
  • 3 — створюємо об'єкт типу User
  • 4 — створюємо об'єкт типу User

3. @DiscriminatorFormula

Але це ще не все. Для нашого дискримінатора ми можемо вказати цілу формулу, за якою він обчислюватиме значення для анотації @DiscriminatorValue.

Для цього є спеціальна анотація, вона так і називається @DiscriminatorFormula.

Приклад:


@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="user_type", discriminatorType = DiscriminatorType.INTEGER)
@DiscriminatorFormula("case when ‘join’ is not null then 1 else 2 end")
@Entity
class User {
  int id;
  String name;
  LocalDate birthday;
}

@Entity
@DiscriminatorValue("1")
class Employee extends User {
 String occupation;
 int salary;
 LocalDate join;
}

@Entity
@DiscriminatorValue("2")
class Client extends User {
   String address;
}

Значення, які повертає @DiscriminatorFormula, будуть порівнюватися Hibernate зі значеннями, вказаними в інструкціях @DiscriminatorValue. З її допомогою можна записувати досить складні сценарії:


@DiscriminatorFormula(
           "case when address is not null " +
           "then 'Client'" +
           "else ("+
           " case when occupation is not null " +
           " then 'Employee' " +
           "else 'Unknown'" +
           "end)" +
           "end"
)