Как смешать стратегии наследования с аннотациями JPA и Hibernate?
Согласно справочной документации по Hibernate, при использовании XML-метаданных Hibernate должно быть возможно смешивать разные стратегии отображения наследования:
http://docs.jboss.org/hibernate/stable/core/reference/en/html/inheritance.html
Тем не менее, соответствующий раздел Справочного руководства по Hibernate Annotations не охватывает следующее: http://docs.jboss.org/hibernate/stable/annotations/reference/en/html/entity.html#d0e1168
С другой стороны, JavaDocs предполагают, что смешивание стратегий наследования должно быть возможным. Например, в javax.persistence.DiscriminatorColumn это говорит:
Стратегия и столбец дискриминатора указываются только в корне иерархии или подчиненной иерархии классов сущностей, в которой применяется другая стратегия наследования.
Ниже приведен пример сопоставления, которого я пытаюсь достичь. Я хотел бы использовать отображение таблицы на подкласс рядом с корнем иерархии, но перейти на отображение таблицы на иерархию классов рядом с листьями. Вот пример кода:
@Entity
@Inheritance( strategy = InheritanceType.JOINED )
public abstract class A implements Serializable
{
@Id
private String id;
// other mapped properties...
}
@Entity
@Inheritance( strategy = InheritanceType.SINGLE_TABLE )
public class BB extends A
{
// other mapped properties and associations...
}
@Entity
public class BB1 extends BB
{
// other stuff, not necessarily mapped...
}
@Entity
public class BB2 extends BB
{
// other stuff, not necessarily mapped...
}
@Entity
@Inheritance( strategy = InheritanceType.SINGLE_TABLE )
public class CC extends A
{
// other mapped properties and associations...
}
@Entity
public class CC1 extends CC
{
// other stuff, not necessarily mapped...
}
...
То, что я ожидаю от этого отображения, имеет ровно 3 таблицы: A
, BB
, а также CC
, И то и другое BB
а также CC
должен иметь столбец дискриминатора по умолчанию DTYPE
, Они также должны предоставлять все столбцы, необходимые для всех сопоставленных свойств и ассоциаций их соответствующих подклассов.
Вместо этого иерархия классов, кажется, использует стратегию наследования таблиц на подклассы повсюду. Т.е. я получаю собственную таблицу для каждой из сущностей, упомянутых выше. Я бы хотел этого избежать, так как листья иерархии классов чрезвычайно легки и кажется просто излишним иметь отдельную таблицу для каждого из них!
Я что-то упустил? Любой совет высоко ценится! Я буду рад предоставить дополнительную информацию...
2 ответа
Согласно справочной документации по Hibernate, при использовании XML-метаданных Hibernate (...) должна быть возможность сочетать разные стратегии отображения наследования.
На самом деле, это не очень поддерживается, они "обманывают", используя вторичную таблицу, чтобы перейти от стратегии единой таблицы в примере документации. Цитирование персистентности Java с помощью Hibernate:
Вы можете отобразить целые иерархии наследования, вложив
<union-subclass>
,<sub- class>
, а также<joined-subclass>
элементы отображения. Вы не можете смешивать их - например, чтобы перейти от иерархии таблиц к классам с дискриминатором к нормализованной стратегии таблиц на подклассы. После того, как вы приняли решение о стратегии наследования, вы должны придерживаться его.Однако это не совсем так. С некоторыми хитростями Hibernate, вы можете переключить стратегию отображения для определенного подкласса. Например, вы можете сопоставить иерархию классов с одной таблицей, но для определенного подкласса переключиться на отдельную таблицу со стратегией сопоставления внешнего ключа, так же как с таблицей на подкласс. Это возможно с
<join>
элемент отображения:<hibernate-mapping> <class name="BillingDetails" table="BILLING_DETAILS"> <id>...</id> <discriminator column="BILLING_DETAILS_TYPE" type="string"/> ... <subclass name="CreditCard" discriminator-value="CC"> <join table="CREDIT_CARD"> <key column="CREDIT_CARD_ID"/> <property name="number" column="CC_NUMBER"/> <property name="expMonth" column="CC_EXP_MONTH"/> <property name="expYear" column="CC_EXP_YEAR"/> ... </join> </subclass> <subclass name="BankAccount" discriminator-value="BA"> <property name=account" column="BA_ACCOUNT"/> ... </subclass> ... </class> </hibernate-mapping>
И вы можете добиться того же с аннотациями:
Java Persistence также поддерживает эту стратегию смешанного отображения наследования с аннотациями. Карта суперкласса
BillingDetails
сInheritanceType.SINGLE_TABLE
, как вы делали раньше. Теперь сопоставьте подкласс, который вы хотите выделить из одной таблицы, с дополнительной таблицей.@Entity @DiscriminatorValue("CC") @SecondaryTable( name = "CREDIT_CARD", pkJoinColumns = @PrimaryKeyJoinColumn(name = "CREDIT_CARD_ID") ) public class CreditCard extends BillingDetails { @Column(table = "CREDIT_CARD", name = "CC_NUMBER", nullable = false) private String number; ... }
Я не проверял это, но вы можете попробовать:
- карта A с использованием стратегии SINGLE_TABLE
- карта BB, CC и т. д. с использованием
@SecondaryTable
аннотаций.
Я не проверял это, я не знаю, будет ли это хорошо работать для BB1, BB2.
Ссылка
- Сохранение Java с помощью Hibernate
- 5.1.5 Смешивание стратегий наследования (p207-p210)
Просто для ясности, вот решение Паскаля, примененное к примеру кода из моего вопроса:
@Entity
@Inheritance( strategy = InheritanceType.SINGLE_TABLE )
@DiscriminatorColumn( name = "entityType",
discriminatorType = DiscriminatorType.STRING )
public abstract class A implements Serializable
{
@Id
private String id;
// other mapped properties...
}
@Entity
@SecondaryTable( name = "BB" )
public class BB extends A
{
@Basic( optional = false)
@Column( table = "BB" )
private String property1;
// other mapped properties and associations...
}
@Entity
public class BB1 extends BB
{
// other stuff, not necessarily mapped...
}
@Entity
public class BB2 extends BB
{
// other stuff, not necessarily mapped...
}
@Entity
@SecondaryTable( name = "CC" )
public class CC extends A
{
@ManyToOne( optional = false)
@JoinColumn( table = "CC" )
private SomeEntity association1;
// other mapped properties and associations...
}
@Entity
public class CC1 extends CC
{
// other stuff, not necessarily mapped...
}
...
Я успешно применил этот подход к своей проблеме, и пока буду придерживаться его. Однако я все еще вижу следующие недостатки:
Столбец дискриминатора находится в главной таблице для иерархии, в таблице для корневого узла
A
, В моем случае было бы достаточно иметь столбец дискриминатора во вторичных таблицахBB
а такжеCC
,В любое время добавляются свойства и ассоциации к подклассам
BB
или жеCC
он / она должен указать, что они должны быть сопоставлены с соответствующей вторичной таблицей. Было бы неплохо, если бы был способ сделать это по умолчанию.