Joined Inheritance with Discriminator

Full source code is provided in the package: de.laliluna.inheritance.joineddiscriminator This mapping is only support, if you use XML mappings. It has the same class hierarchy as our last example. This approach is a combination of the two former examples. We have a discriminator as in the single table example and a table structure as in the last example. We combine a subclass with a join.

images/mapping/inheritance-joined-discriminator.png

<hibernate-mapping package="de.laliluna.inheritance.joineddiscriminator">
  <class name="MusicFan" table="tmusicfan">
........ snip ..........
    <set name="musicGroups" table="musicfan_musicgroup">
      <key column="musicfan_id"></key>
      <many-to-many class="MusicGroup">
        <column name="musicgroup_id"></column>
      </many-to-many>
    </set>
  </class>
  <class name="MusicGroup" table="tmusicgroup">
........ snip ..........
    <discriminator column="discriminator"></discriminator>
    <property name="name" type="string"></property>

    <subclass name="BoyGroup" discriminator-value="boygroup">
     <join table="tboygroup">
        <key column="musicgroup_id"></key>
        <property name="cryingGroupies" type="boolean"></property>
      </join>

    </subclass>
    <subclass name="HardrockGroup" discriminator-value="hardrock">
      <join table="thardrock">
        <key column="musicgroup_id"></key>
        <property name="destroyedGuitars" type="integer" not-null="true"/>
      </join>
    </subclass>
  </class>
</hibernate-mapping>

To be aware of possible performance issues, I will explain you the behaviour of this mapping. When we insert an object of the subclass boygroup, Hibernate will generate two inserts. Common attributes are saved in the table tmusicgroup. Subclass specific attributes are saved in the table of the subclass.

insert into tmusicgroup (name, discriminator, id) values (?, 'boygroup', ?)
insert into tboygroup (cryingGroupies, musicgroup_id) values (?, ?)

When we select data from a subclass, we always need a join.

session.createQuery("from HardrockGroup").list();

Resulting query:

select hardrockgr0_.id as id45_,
hardrockgr0_.name as name45_,
hardrockgr0_1_.destroyedGuitars as destroye2_47_
from
tmusicgroup hardrockgr0_
inner join thardrock hardrockgr0_1_ on
   hardrockgr0_.id=hardrockgr0_1_.musicgroup_id
where hardrockgr0_.discriminator='hardrock'

When we select data form the parent class all tables are joined.

session.createQuery("from MusicGroup").list();

Resulting query:

select
musicgroup0_.id as id45_,
musicgroup0_.name as name45_,
musicgroup0_1_.cryingGroupies as cryingGr2_46_,
musicgroup0_2_.destroyedGuitars as destroye2_47_,
musicgroup0_.discriminator as discrimi2_45_
from
tmusicgroup musicgroup0_
left outer join tboygroup musicgroup0_1_
   on musicgroup0_.id=musicgroup0_1_.musicgroup_id
left outer join thardrock musicgroup0_2_
   on musicgroup0_.id=musicgroup0_2_.musicgroup_id

The key difference between this approach and xref:inheritanceonetableperclass1 is the use of the discriminator column in queries.

session.createQuery("from MusicGroup mg where  mg.destroyedGuitars>150")
   .list();

would result in the following query for the current approach.

select musicgroup0_.id as id45_, musicgroup0_.name as name45_,
musicgroup0_1_.cryingGroupies as cryingGr2_46_,
musicgroup0_2_.destroyedGuitars as destroye2_47_,
musicgroup0_.discriminator as discrimi2_45_
from
tmusicgroup musicgroup0_
left outer join tboygroup musicgroup0_1_
   on musicgroup0_.id=musicgroup0_1_.musicgroup_id
left outer join thardrock musicgroup0_2_
   on musicgroup0_.id=musicgroup0_2_.musicgroup_id
where musicgroup0_2_.destroyedGuitars>150

The query is considerably faster as compared to the normal joined apporach. For a test I run with about 20.000 music groups, equally divided into boygroups and hardrock groups. It becomes even faster when you explicitly specify the class.

list = session.createQuery(
 "from MusicGroup mg where mg.class = HardrockGroup and mg.destroyedGuitars>150")
 .list();

You might consider to use this approach instead of if select performance is important.