Как смешать стратегии наследования с аннотациями 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он / она должен указать, что они должны быть сопоставлены с соответствующей вторичной таблицей. Было бы неплохо, если бы был способ сделать это по умолчанию.

Другие вопросы по тегам