1. Введение
Наследование является одним из ключевых понятий в Java. Поэтому неудивительно, что большинство моделей предметной области используют его. Но, к сожалению, эта концепция не существует в реляционных базах данных, и вам нужно найти способ отобразить иерархию наследования на модель реляционной таблицы.
JPA и Hibernate поддерживают разные стратегии, которые сопоставляют иерархию наследования с различными моделями таблиц. Давайте взглянем на главу моей новой книги «Советы по Hibernate — более 70 решений распространенных проблем Hibernate», в которой я объясняю стратегию SingleTable
. Он сопоставляет все классы иерархии наследования с одной и той же таблицей базы данных.
Я объясняю другие стратегии сопоставления наследования в Hibernate в моей книге Hibernate Tips . Это кулинарная книга с более чем 70 готовыми к использованию рецептами по таким темам, как базовые и расширенные сопоставления, ведение журналов, поддержка Java 8, кэширование, а также статически и динамически определяемые запросы. Вы можете приобрести его на этой неделе на Amazon по специальной стартовой цене всего в 2,99 доллара.
2. Советы по Hibernate — как сопоставить иерархию наследования с одной таблицей
2.1. Проблема
Моя база данных содержит одну таблицу, которую я хочу сопоставить с иерархией наследования сущностей. Как определить такое отображение?
2.2. Решение
JPA и Hibernate поддерживают разные стратегии наследования, которые позволяют отображать объекты в разные структуры таблиц. Стратегия SingleTable
является одной из них и отображает иерархию наследования сущностей в одну таблицу базы данных.
Давайте взглянем на модель сущностей, прежде чем я объясню детали стратегии SingleTable
. Авторы
могут создавать различные виды публикаций
, такие как книги
и сообщения в блогах
. Класс Publication
является надклассом классов Book
и BlogPost
.
Стратегия SingleTable
сопоставляет три объекта иерархии наследования с таблицей публикации .
Если вы хотите использовать эту стратегию наследования, вам необходимо аннотировать суперкласс аннотацией @Inheritance
и предоставить InheritanceType.SINGLE_TABLE
в качестве значения атрибута стратегии .
Вы также можете аннотировать суперкласс аннотацией @DiscriminatorColumn
, чтобы определить имя значения дискриминатора. Hibernate использует это значение для определения объекта, которому он должен сопоставить запись базы данных. Если вы не определяете столбец дискриминатора, как я делаю в следующем фрагменте кода, Hibernate и все другие реализации JPA используют столбец DTYPE
.
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class Publication {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id", updatable = false, nullable = false)
private Long id;
@Version
private int version;
private String title;
private LocalDate publishingDate;
@ManyToMany
@JoinTable(
name="PublicationAuthor",
joinColumns={@JoinColumn(name="publicationId", referencedColumnName="id")},
inverseJoinColumns={@JoinColumn(name="authorId", referencedColumnName="id")})
private Set<Author> authors = new HashSet<Author>();
...
}
Подклассы должны расширять суперкласс, и вам нужно аннотировать их аннотацией @Entity
.
Спецификация JPA также рекомендует аннотировать его аннотацией @DiscriminatorValue
, чтобы определить значение дискриминатора для этого класса объектов. Если вы не предоставите эту аннотацию, ваша реализация JPA создаст значение дискриминатора.
Но спецификация JPA не определяет, как генерировать значение дискриминатора, и ваше приложение может быть несовместимо с другими реализациями JPA. Hibernate использует простое имя объекта в качестве дискриминатора.
@Entity
@DiscriminatorValue("Book")
public class Book extends Publication {
private int numPages;
...
}
Стратегия SingleTable
не требует, чтобы Hibernate генерировал какие-либо сложные запросы, если вы хотите выбрать определенный объект, выполнить полиморфный запрос или пройти полиморфную ассоциацию.
Author a = em.find(Author.class, 1L);
List<Publication> publications = a.getPublications();
Все сущности хранятся в одной таблице, и Hibernate может выбирать их оттуда без дополнительного предложения JOIN
.
15:41:28,379 DEBUG [org.hibernate.SQL] -
select
author0_.id as id1_0_0_,
author0_.firstName as firstNam2_0_0_,
author0_.lastName as lastName3_0_0_,
author0_.version as version4_0_0_
from
Author author0_
where
author0_.id=?
15:41:28,384 DEBUG [org.hibernate.SQL] -
select
publicatio0_.authorId as authorId2_2_0_,
publicatio0_.publicationId as publicat1_2_0_,
publicatio1_.id as id2_1_1_,
publicatio1_.publishingDate as publishi3_1_1_,
publicatio1_.title as title4_1_1_,
publicatio1_.version as version5_1_1_,
publicatio1_.numPages as numPages6_1_1_,
publicatio1_.url as url7_1_1_,
publicatio1_.DTYPE as DTYPE1_1_1_
from
PublicationAuthor publicatio0_
inner join
Publication publicatio1_
on publicatio0_.publicationId=publicatio1_.id
where
publicatio0_.authorId=?
2.3. Исходный код
Ссылку на скачивание проекта с исполняемыми тест-кейсами для этого совета по Hibernate вы можете найти в книге .
2.4. Учить больше
Вы также можете сопоставить сущности иерархии наследования с несколькими таблицами базы данных.
Я покажу вам, как это сделать, в главе Как отобразить иерархию наследования на несколько таблиц
.
3. Резюме
Как вы видели в этом совете по Hibernate, JPA и Hibernate предоставляют простой способ сопоставления иерархии наследования с одной таблицей базы данных. Вам просто нужно аннотировать суперкласс с помощью @Inheritance(strategy = InheritanceType.SINGLE_TABLE)
, а также вы должны аннотировать подклассы с помощью @DiscriminatorValue("Book")
.
Больше подобных рецептов вы можете найти в моей новой книге « Советы по Hibernate: более 70 решений распространенных проблем с Hibernate » . Он дает вам более 70 готовых к использованию рецептов по таким темам, как базовые и расширенные сопоставления, ведение журналов, поддержка Java 8, кэширование, а также статически и динамически определенные запросы. Всего за несколько дней вы можете получить электронную книгу за 2,99 доллара и книгу в мягкой обложке за 12,99 доллара на Amazon .