Как смешать стратегии наследования с аннотациями JPA и Hibernate? - PullRequest
31 голосов
/ 12 октября 2010

Согласно Справочной документации Hibernate должна быть возможность смешивать различные стратегии отображения наследования при использовании XML-метаданных Hibernate:
http://docs.jboss.org/hibernate/stable/core/reference/en/html/inheritance.html#inheritance-mixing-tableperclass-tablepersubclass

Однако соответствующий раздел Справочное руководство по Hibernate не распространяется на:
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 ]

27 голосов
/ 12 октября 2010

Согласно справочной документации 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)
24 голосов
/ 13 октября 2010

Просто для ясности, вот решение Паскаля, примененное к примеру кода из моего вопроса:

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

...