Joined Inheritance

Full source code is provided in the package: de.laliluna.inheritance.joined

Joined Inheritance tables

The parent class holding common attributes (id, name) is saved in the mouse table. The individual tables share the same primary key with the parent class table.

This approach is fully normalized. We do not need a discriminator column. Hibernate works out the type by clever SQL queries.

Annotation mapping. 

import javax.persistence.Entity;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
....... snip  ....

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Mouse {
    @Id @GeneratedValue
    private Integer id;
    private String name;

@Inheritance(strategy = InheritanceType.JOINED) specifies the inheritance strategy. The subclasses do not have any inheritance related annotations.

@Entity
public class KitchenMouse extends Mouse{

    private String favouriteCheese;
......
@Entity
public class LibraryMouse extends Mouse{

    private String favouriteBook;
.....

XML mapping. 

<hibernate-mapping package="de.laliluna.inheritance.joined">
  <class name="Mouse">
....... snip ...........
    <joined-subclass name="KitchenMouse">
      <key column="mouse_id"></key>
      <property name="favouriteBook" column="favourite_book"/>
    </joined-subclass>
    <joined-subclass name="LibraryMouse" table="library_mouse">
      <key column="mouse_id"></key>
      <property name="favouriteCheese" column="favourite_cheese"/>
    </joined-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 LibraryMouse, Hibernate will generate two inserts. Common attributes are saved in the table mouse. Subclass specific attributes are saved in the table of the subclass.

insert into Mouse (name, id) values (?, ?)
insert into LibraryMouse (favouriteBook, id) values (?, ?)

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

session.createQuery("from LibraryMouse m ").list();

Resulting SQL query:

select
    kitchenmou0_.id as id5_,
    kitchenmou0_1_.name as name5_,
    kitchenmou0_.favouriteCheese as favourit1_6_
from
    KitchenMouse kitchenmou0_
inner join
    Mouse kitchenmou0_1_
        on kitchenmou0_.id=kitchenmou0_1_.id

Selecting the parent class will result in a big join of all subclasses.

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

Resulting SQL query:

select
    mouse0_.id as id5_,
    mouse0_.name as name5_,
    mouse0_1_.favouriteCheese as favourit1_6_,
    mouse0_2_.favouriteBook as favourit1_7_,
    case
        when mouse0_1_.id is not null then 1
        when mouse0_2_.id is not null then 2
        when mouse0_.id is not null then 0
    end as clazz_
from
    Mouse mouse0_
left outer join
    KitchenMouse mouse0_1_
        on mouse0_.id=mouse0_1_.id
left outer join
    LibraryMouse mouse0_2_
        on mouse0_.id=mouse0_2_.id

As in our previous example, other classes can have a relation to the parent class or to one of the sub classes.

Samples of use. 

/* create and set relation */
House house = new House();
Mouse bea = new Mouse("Bea");
house.getMice().add(bea);
KitchenMouse john = new KitchenMouse("John");
house.getMice().add(john);
LibraryMouse tim = new LibraryMouse("Tim");
house.getMice().add(tim);
session.save(bea);
session.save(john);
session.save(tim);
session.save(house);

/* get all kind of mice*/
List<Mouse> result = session.createQuery("select m from Mouse m")
   .list();

/* select all kitchen mice who like Gauda cheese blue flowers */
List<KitchenMouse result = session
   .createQuery("select m from KitchenMouse m where m.favouriteCheese ='Gauda'")
   .list();

/* select all mice of type LibraryMouse */
List<LibraryMouse> result = session
   .createQuery("select m from Mouse m where type(m) = LibraryMouse ")
   .list();