← Назад к вопросам
Как представить наследование в базе данных
1.7 Middle🔥 121 комментариев
#ORM и Hibernate#Базы данных и SQL#ООП
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Представление наследования в базе данных
Это классическая архитектурная задача на пересечении ООП (Java) и реляционных БД. Существует три основных подхода, каждый с плюсами и минусами.
1. Single Table Inheritance (STI) — одна таблица для всех
Идея: вся иерархия классов хранится в одной таблице с колонкой-дискриминатором.
CREATE TABLE employees (
id BIGINT PRIMARY KEY,
type VARCHAR(50) NOT NULL, -- дискриминатор
name VARCHAR(255),
salary DECIMAL(10, 2),
department_id BIGINT, -- для Manager
years_experience INT -- для Developer
);
Java код:
@Entity
@Table(name = "employees")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.STRING)
public abstract class Employee {
@Id
private Long id;
private String name;
private BigDecimal salary;
}
@Entity
@DiscriminatorValue("MANAGER")
public class Manager extends Employee {
@ManyToOne
@JoinColumn(name = "department_id")
private Department department;
}
@Entity
@DiscriminatorValue("DEVELOPER")
public class Developer extends Employee {
private Integer yearsExperience;
}
Плюсы:
- Быстрые запросы (нет JOIN)
- Простая логика
- Легко добавить новый тип
Минусы:
- Много NULL колонок (если иерархия разветвлённая)
- Слабая типизация на уровне БД
- Можно потеряться в смысле таблицы
2. Joined Table Inheritance (JTI) — таблица на класс
Идея: каждый класс имеет свою таблицу, связанные по первичному ключу.
CREATE TABLE employees (
id BIGINT PRIMARY KEY,
name VARCHAR(255),
salary DECIMAL(10, 2)
);
CREATE TABLE managers (
id BIGINT PRIMARY KEY,
department_id BIGINT,
FOREIGN KEY (id) REFERENCES employees(id) ON DELETE CASCADE
);
CREATE TABLE developers (
id BIGINT PRIMARY KEY,
years_experience INT,
FOREIGN KEY (id) REFERENCES employees(id) ON DELETE CASCADE
);
Java код:
@Entity
@Table(name = "employees")
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class Employee {
@Id
private Long id;
private String name;
private BigDecimal salary;
}
@Entity
@Table(name = "managers")
public class Manager extends Employee {
@ManyToOne
@JoinColumn(name = "department_id")
private Department department;
}
@Entity
@Table(name = "developers")
public class Developer extends Employee {
private Integer yearsExperience;
}
Плюсы:
- Нормализованная схема
- Чистая типизация
- Хорошо масштабируется
Минусы:
- Много JOIN для получения полного объекта
- Медленнее чем STI на больших объёмах
- Сложнее с полиморфными запросами
3. Table Per Class (TPC) — отдельная таблица с дублированием
Идея: каждый конкретный класс имеет полную таблицу (без базовой таблицы).
CREATE TABLE managers (
id BIGINT PRIMARY KEY,
name VARCHAR(255),
salary DECIMAL(10, 2),
department_id BIGINT
);
CREATE TABLE developers (
id BIGINT PRIMARY KEY,
name VARCHAR(255),
salary DECIMAL(10, 2),
years_experience INT
);
Java код:
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Employee {
@Id
private Long id;
private String name;
private BigDecimal salary;
}
@Entity
@Table(name = "managers")
public class Manager extends Employee {
@ManyToOne
@JoinColumn(name = "department_id")
private Department department;
}
@Entity
@Table(name = "developers")
public class Developer extends Employee {
private Integer yearsExperience;
}
Плюсы:
- Каждая таблица независима
- Полиморфные запросы не требуют UNION
Минусы:
- Дублирование данных и логики
- Сложно обновлять базовые свойства
- Нарушает нормализацию
- Генерирует сложный SQL (UNION по всем таблицам)
Рекомендации для выбора
| Сценарий | Стратегия | Причина |
|---|---|---|
| Простая иерархия (2-3 типа) | STI | Быстро, просто, нет NULL |
| Сложная иерархия, разные поля | JTI | Нормализация, чистота схемы |
| Полиморфные запросы часто | STI | Один SELECT вместо UNION |
| Высокие требования к производительности | STI | Минимум JOIN |
| Важна типизация БД | JTI | Отдельные таблицы явно показывают типы |
Я лично предпочитаю JTI в большинстве случаев: нормализованная схема, хорошо масштабируется, понятна новым разработчикам.
Гибридный подход
Иногда комбинируют стратегии:
// Сильно разветвлённая иерархия
// Верхний уровень: JTI
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class Employee {...}
// Нижний уровень: STI в отдельных таблицах
@Entity
@Table(name = "developers")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "dev_type")
public abstract class Developer extends Employee {...}
@Entity
@DiscriminatorValue("BACKEND")
public class BackendDeveloper extends Developer {...}
Это достаточно редко используется, но мощно для сложных доменов.