According to the Hibernate Reference Documentation it should be possible to mix different inheritance mapping strategies when using Hibernate's XML-Metadata:
http://docs.jboss.org/hibernate/stable/core/reference/en/html/inheritance.html#inheritance-mixing-tableperclass-tablepersubclass
However, the corresponding section of the Hibernate Annotations Reference Guide does not cover that:http://docs.jboss.org/hibernate/stable/annotations/reference/en/html/entity.html#d0e1168
On the other hand, the JavaDocs suggest that mixing inheritance strategies should be possible. For instance in javax.persistence.DiscriminatorColumn it says:
The strategy and the discriminator column are only specified in the root of an entity class hierarchy or subhierarchy in which a different inheritance strategy is applied.
The following is an example for the mapping I'm trying to achieve. I'd like to use table-per-subclass mapping near the root of the hierarchy, but change to table-per-class-hierarchy mapping near the leaves. Here's some example code:
@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... } ...
What I expect from this mapping is having exactly 3 tables: A
, BB
, and CC
. Both BB
and CC
should have a default discriminator column called DTYPE
. They should also provide all columns necessary for all mapped properties and associations of their respective subclasses.
Instead , the class hierarchy seems to use the table-per-subclass inheritance strategy throughout. I.e. I get an own table for each of the entities mentioned above. I'd like to avoid this, since the leaves of the class-hierarchy are extremely light-weight and it just seems overkill to have a separate table for each of them!
Did I overlook something? Any advice is highly appreciated! I'll be glad to provide additional info...
The default strategy, InheritanceType. SINGLE_TABLE, is used if the @Inheritance annotation is not specified on the root class of the entity hierarchy.
Single Table. The single table strategy maps all entities of the inheritance structure to the same database table. This approach makes polymorphic queries very efficient and provides the best performance.
Inheritance is the core concept of object oriented language, therefore we can use inheritance relationships or strategies between entities. JPA support three types of inheritance strategies such as SINGLE_TABLE, JOINED_TABLE, and TABLE_PER_CONCRETE_CLASS.
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
withInheritanceType.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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With