Как смешивать стратегии наследования с аннотациями JPA и Hibernate?
В соответствии с Hibernate Reference Documentation при использовании XML-метаданных Hibernate должно быть возможно смешать различные стратегии сопоставления наследования:
http://docs.jboss.org/hibernate/stable/core/reference/en/html/inheritance.html#inheritance-mixing-tableperclass-tablepersubclass
Однако в соответствующем разделе справочника 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...
}
...
То, что я ожидаю от этого сопоставления, имеет ровно три таблицы: A
, BB
и CC
. Оба BB
и CC
должны иметь столбец дискриминатора по умолчанию, называемый DTYPE
. Они также должны предоставить все столбцы, необходимые для всех сопоставленных свойств и ассоциаций их соответствующих подклассов.
Вместо этого иерархия классов, похоже, использует стратегию наследования на основе подкласса на всем протяжении. То есть Я получаю собственную таблицу для каждого из перечисленных выше объектов. Я бы хотел этого избежать, так как листы иерархии классов чрезвычайно легки, и для каждых из них кажется излишним отдельная таблица!
Я что-то упустил? Любые советы приветствуются! Я буду рад предоставить дополнительную информацию...
Ответы
Ответ 1
В соответствии с Hibernate Reference Documentation при использовании Hibernate XML-метаданных (...)
должно быть возможно смешать различные стратегии сопоставления наследования.
На самом деле это не поддерживается, они "обманывают", используя вторичную таблицу, чтобы перейти от стратегии единой таблицы в примере документации. Цитата о сохранении Java с Hibernate:
Вы можете отобразить целое наследование иерархии путем вложения <union-subclass>
, <sub- class>
, и <joined-subclass>
элементы. Вы не можете их смешивать - для Например, чтобы переключиться с иерархия таблицы за класс с дискриминатор к нормированному стратегия за один подкласс. После вы приняли решение стратегии наследования, вы должны придерживайтесь его.
Однако это не совсем верно. С некоторыми хитроумными трюками вы можете переключить стратегию сопоставления для конкретный подкласс. Например, вы может сопоставить иерархию классов с одним таблицу, но для конкретного подкласса, переключитесь на отдельную таблицу с стратегия сопоставления внешнего ключа, так же как и с таблицей на подкласс. Это возможно с отображением <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;
...
}
Я не тестировал это, но вы могли бы попробовать:
- map A с использованием стратегии SINGLE_TABLE
- отображение BB, CC и т.д. с помощью аннотации
@SecondaryTable
.
Я не тестировал это, я не знаю, будет ли он работать хорошо для BB1, BB2.
Ссылка
- Сохранение Java с помощью Hibernate
- 5.1.5 Смешивание стратегий наследования (p207-p210)
Ответ 2
Просто для ясности, вот решение Pascal применительно к примерному коду из моего вопроса:
@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...
}
...
Я успешно применил этот подход к своей проблеме, и пока буду придерживаться его. Однако я все еще вижу следующие недостатки:
-
Столбец дискриминатора находится в главной таблице для иерархии, таблица для root-enity A
. В моем случае достаточно иметь столбец дискриминатора во вторичных таблицах BB
и CC
.
-
В любое время добавляются свойства и ассоциации к подклассам BB
или CC
, он должен указать, что они должны быть сопоставлены с соответствующей вторичной таблицей. Было бы неплохо, если бы был способ сделать это по умолчанию.