How to mix inheritance strategies with JPA annotations and Hibernate?

According to the Hibernate Reference Documentation it should be possible to mix different inheritance mapping strategies when using Hibernate’s XML-Metadata (…)

Actually, it’s not really supported, they are “cheating” using a secondary table to switch from the single table strategy in the example of the documentation. Quoting Java Persistence with Hibernate:

You can map whole inheritance
hierarchies by nesting
<union-subclass>, <sub- class>,
and <joined-subclass> mapping
elements. You can’t mix them — for
example, to switch from a
table-per-class hierarchy with a
discriminator to a normalized
table-per-subclass strategy. Once
you’ve made a decision for an
inheritance strategy, you have to
stick to it
.

This isn’t completely true, however.
With some Hibernate tricks, you can
switch the mapping strategy for a
particular subclass. For example, you
can map a class hierarchy to a single
table, but for a particular subclass,
switch to a separate table with a
foreign key mapping strategy
, just as
with table per subclass. This is
possible with the <join> mapping
element:

<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>

And you could achieve the same with annotations:

Java Persistence also supports this mixed inheritance mapping strategy with annotations. Map the superclass BillingDetails with InheritanceType.SINGLE_TABLE, as you did before. Now map the subclass you want to break out of the single table to a secondary 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;
    ...
}

I didn’t test this but you could maybe try to:

  • map A using a SINGLE_TABLE strategy
  • map BB, CC, etc using the @SecondaryTable annotation.

I’ve not tested this, I don’t know if it will work well for BB1, BB2.

Reference

  • Java Persistence with Hibernate
    • 5.1.5 Mixing inheritance strategies (p207-p210)

Leave a Comment