According to the Hibernate Reference Documentation it should be possible to mix different inheritance mapping strategies when using Hibernate\'s XML-Metadata:
h
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
BillingDetailswithInheritanceType.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:
@SecondaryTable annotation. I've not tested this, I don't know if it will work well for BB1, BB2.
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.