How to mix inheritance strategies with JPA annotations and Hibernate?

后端 未结 2 1582
春和景丽
春和景丽 2020-11-27 04:36

According to the Hibernate Reference Documentation it should be possible to mix different inheritance mapping strategies when using Hibernate\'s XML-Metadata:
h

相关标签:
2条回答
  • 2020-11-27 05:12

    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)
    0 讨论(0)
  • 2020-11-27 05:25

    Just for the sake of clarity, here is Pascal's solution applied to the example code from my question:

    @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...
    }
    
    ...
    

    I've successfully applied this approach to my problem, and I'll stick to it for the time being. However I still see the following disadvantages:

    • The discriminator column is located in the main table for the hierarchy, the table for root-enity A. In my case, it would be sufficient to have the discriminator column in the secondary tables BB and CC.

    • Anytime one adds properties and associations to subclasses of BB or CC, he/she has to specify that they should be mapped to the respective secondary table. Would be nice, if there was a way to make that the default.

    0 讨论(0)
提交回复
热议问题