Подтвердить что ты не робот

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

Вместо этого иерархия классов, похоже, использует стратегию наследования на основе подкласса на всем протяжении. То есть Я получаю собственную таблицу для каждого из перечисленных выше объектов. Я бы хотел этого избежать, так как листы иерархии классов чрезвычайно легки, и для каждых из них кажется излишним отдельная таблица!


Я что-то упустил? Любые советы приветствуются! Я буду рад предоставить дополнительную информацию...

4b9b3361

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