Описание

Следующий подход к хранению иерархии классов – это хранить все классы иерархии в одной таблице. Такая стратегия называется Single Table.

Например, так:


CREATE TABLE user_ employee_client {
  id INT,
  name VARCHAR,
  birthday DATE,
  occupation VARCHAR,
  salary INT,
  join DATE,
  address VARCHAR,
  DTYPE VARCHAR
}

То есть у нас есть одна таблица, у которой колонки для всех классов нашей иерархии обозначены разными цветами. Также есть специальная служебная колонка DTYPE VARCHAR, где Hibernate будет хранить имя Entity-класса.

Дело осталось за малым – объяснить Hibernate, что данные Entity-классов теперь хранятся в базе в одной таблице. Сделать это можно с помощью аннотации @Inheritance:


@Inheritance(strategy = InheritanceType.SINGLE_TABLE)

Пример наших классов:


@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@Entity
class User {
  int id;
  String name;
  LocalDate birthday;
}

@Entity
class Employee extends User {
 	String occupation;
 	int salary;
 	LocalDate join;
}

@Entity
class Client extends User {
   String address;
}

Как данные сохраняются

Теперь давай напишем пример, где создадим парочку наших сущностей и сохраним их в базу:


Employee employee = new Employee();
employee.id = 101;
employee.name = "Иванов";
employee.birthday = LocalDate.of("01-01-1999");
employee.occupation = "Программист"
employee.salary = 100000;
employee.join = LocalDate.of("12-01-2018");
session.persist(employee);
 
Client client = new Client();
client.id = 102;
client.name = "Петров";
client.birthday = LocalDate.of("15-11-1988");
client.address = "Шандара";
session.persist(client);

При сохранении в базу данных будет выполнен такой SQL-запрос:


INSERT INTO user_ employee_client (id, name, birthday, occupation, salary, join, DTYPE)
VALUES (101, 'Иванов', '01-01-1999', 'Программист', 100000, '12-01-2018', 'Employee')
 
INSERT INTO user_ employee_client (id, name, birthday, address, DTYPE)
VALUES (102, 'Петров', '15-11-1988', 'Шандара', 'Client')

При сохранении данных в таблицу Hibernate передает только известные ему поля сущностей. Это означает, что неуказанные колонки будут иметь значение NULL.

А это значит, что ты не можешь указать для колонки occupation тип NOT NULL, так как при хранении клиента в этой же таблице его occupation будет NULL. Это один из минусов хранения разных сущностей в одной таблице.

Последнее поле в SQL-запросе – это колонка DTYPE, в ней передается имя Entity-класса. Он используется Hibernate, когда ты хочешь прочитать данные из своей таблицы.

Пример:


 List<User> accounts = session.createQuery("from User").list();

Данный запрос вернет список всех сохраненных в базе объектов типа пользователь: User, Employee и Client. На основе колонки DTYPE будет правильно определен тип сущности и создан объект правильного класса.

В нашем случае в списке accounts будут два объекта: типа Employee и типа Client.

HQL рулит.