Mapping class inheritance in Hibernate 5

In this post I want to present four basic strategies for mapping class inheritance in Hibernate:

  1. No inheritance - just copy superclass properties to subclasses
  2. Table per class hierarchy
  3. Table per concrete class
  4. Table per every class

No inheritance

This strategy is used if we want to share Java code between entity classes. An example will show us how it works. Let’s say we want to avoid declaring id and version fields in every entity class. We can solve this by creating abstract superclass BaseEntity that will hold common code and annotating it with @MappedSuperclass to enable no inheritance strategy. Here is BaseEntity class code:

@MappedSuperclass
public abstract class BaseEntity {
    @Id
    @GeneratedValue
    private Long id;

    @Version
    private long version;

    // getters/setters etc.
}

Now we may create two independent entity classes User and Product that will inherit id and version fields with mappings from BaseEntity:

@Entity
@Table(name = "\"user\"")
public class User extends BaseEntity {
    private String username;

    @org.hibernate.annotations.Type(type = "yes_no")
    private boolean isAdmin;

    // constructor/getters/setters etc.
}
@Entity
public class Product extends BaseEntity {
    public String name;
    public BigDecimal price;

    // constructor/getters/setters etc.
}

For given classes Hibernate will generate database schema: DB Schema generated for User and Product

Since this strategy is used to only share Java code we should not query database for BaseEntity instances. If we do Hibernate will execute many select statements - one for every class inheriting from BaseEntity. For example:

entityManager.unwrap(Session.class)
        .createCriteria(BaseEntity.class)
        .list();

Will result in queries:

select
    this_.id as id1_0_0_,
    this_.version as version2_0_0_,
    this_.name as name3_0_0_,
    this_.price as price4_0_0_ 
from
    product this_

select
    this_.id as id1_1_0_,
    this_.version as version2_1_0_,
    this_.isAdmin as isAdmin3_1_0_,
    this_.username as username4_1_0_ 
from
    "user" this_

NOTE: Querying for BaseEntity via JPA will throw exception with message Not an entity: class BaseEntity. We can query for BaseEntity only via Hibernate Session object.

We will use our BaseEntity class in the example code of the remaining strategies to show that it can be mixed with “real” ORM inheritance.

Table per class hierarchy

In this strategy all subclasses data will be stored in single table. A special column called discriminator is added to that table to help Hibernate know which subclass is stored in given row.

An example will show how it works. We start by creating a superclass called Animal:

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "animal_type")
public abstract class Animal extends BaseEntity {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

And two subclasses Cat and Dog:

@Entity
@DiscriminatorValue("cat")
public class Cat extends Animal {
    private boolean isPurring;

    // getters/setter etc.
}
@Entity
@DiscriminatorValue("dog")
public class Dog extends Animal {
    private boolean isBarking;

    // getters/setter etc.
}

To enable table per hierarchy strategy, superclass must be marked as @Entity and must have @Inheritance(strategy = InheritanceType.SINGLE_TABLE) annotation. We can choose discriminator column name and type using @DiscriminatorColumn annotation. Subclasses must be marked as @Entity and can provide values for discriminator column via @DiscriminatorValue annotation (discriminator value defaults to class name).

For this example Hibernate will generate schema: Schema generated by Hibernate

Let’s save some data:

Cat cat = new Cat();
cat.setName("kitty");
cat.setPurring(true);
entityManager.persist(cat);

Dog dog = new Dog();
dog.setName("barky");
dog.setBarking(false);
entityManager.persist(dog);

And check how they are stored in database: Data stored in database

Saving was easy, now let’s check querying. We start by getting all Animal instances from database:

entityManager.createQuery("from Animal")
        .getResultList()
        .forEach(a -> System.out.println(a));

This will result in SQL query:

select
    animal0_.id as id2_0_,
    animal0_.version as version3_0_,
    animal0_.name as name4_0_,
    animal0_.isBarking as isBarkin5_0_,
    animal0_.isPurring as isPurrin6_0_,
    animal0_.animal_type as animal_t1_0_ 
from
    Animal animal0_

With this strategy we may also query specific animal types e.g. cats:

entityManager.createQuery(
    "select c from Cat c where c.isPurring = true")

This will result in the following SQL:

select
    cat0_.id as id2_0_,
    cat0_.version as version3_0_,
    cat0_.name as name4_0_,
    cat0_.isPurring as isPurrin6_0_ 
from
    Animal cat0_ 
where
    cat0_.animal_type='cat' 
    and cat0_.isPurring=true

We can see that Hibernate added test for discriminator column animal_type='cat' to limit returned animals to cats only.

Before we move to next strategy let’s see what are pros and cons of table per hierarchy strategy:

pros
Fast - no joins are needed to retrieve data
Simple - only single table is needed in database
cons
Cannot create constrains in database - all columns representing subclass data must be nullable. This is serious drawback because without constrains data can be easily corrupted by application bug or by inattentive users
Wasted space - when subclasses have many fields shared table will contain many columns most of which will contain NULLs

Table per concrete class

This inheritance strategy will generate database table per each concrete class in the hierarchy. Let’s demonstrate on example. Given classes (abstract classes are in the blue boxes): UML diagram of classes used in this example This strategy will generate tables for Dog, GrumpyCat and Kitten classes.

Here is code for all classes used in this example:

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Animal extends BaseEntity {
    private String name;
    // ...
}

@Entity
public class Dog extends Animal {
    private boolean isBarking;
    // ...
}

@Entity
public abstract class AbstractCat extends Animal {
    private boolean isPurring;
    // ...
}

@Entity
public class GrumpyCat extends AbstractCat {
    private int grumpiness;
    // ...
}

@Entity
public class Kitten extends AbstractCat {
    public int sweetness;
    // ...
}

To enable table per concrete class strategy, root of the inheritance hierarchy must be marked as @Entity and must have @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) annotation. All subclasses (even abstract ones) must be marked as @Entity. Abstract classes will not be represented in database.

For our Animal example Hibernate will generate database schema: Schema generated by Hibernate for our animal example

When we query for all instances of Animal:

entityManager.createQuery("from Animal")
        .getResultList();

Hibernate uses SQL subquery and union all operator to gather rows from all tables containing subclasses data. To differentiate between subclasses in the result set, Hibernate adds special column clazz_ that will contain different numbers for different subclasses:

select
        animal0_.id as id1_1_, animal0_.version as version2_1_, animal0_.name as name3_1_, animal0_.isBarking as isBarkin1_2_, animal0_.isPurring as isPurrin1_0_, animal0_.grumpiness as grumpine1_3_, animal0_.sweetness as sweetnes1_4_, 
        animal0_.clazz_ as clazz_ 
    from
        ( select
            id, version, name, null::boolean as isBarking, isPurring, grumpiness, null::int4 as sweetness, 
            3 as clazz_ 
        from
            GrumpyCat 

        union all 
        
        select
            id, version, name, null::boolean as isBarking, isPurring, null::int4 as grumpiness, sweetness, 
            4 as clazz_ 
        from
            Kitten
        
        union all 

        select
            id, version, name, isBarking, null::boolean as isPurring, null::int4 as grumpiness, null::int4 as sweetness, 
            1 as clazz_ 
        from
            Dog 
    ) animal0_

When we query for one of the concrete subclasses Hibernate will directly query table containing that subclass data. For example:

entityManager.createQuery(
        "select gc from GrumpyCat gc where gc.grumpiness > 0"
    ).getResultList()

Will generate SQL query:

select
    grumpycat0_.id as id1_1_,
    grumpycat0_.version as version2_1_,
    grumpycat0_.name as name3_1_,
    grumpycat0_.isPurring as isPurrin1_0_,
    grumpycat0_.grumpiness as grumpine1_3_ 
from
    GrumpyCat grumpycat0_ 
where
    grumpycat0_.grumpiness>0

Before we move to the next strategy let’s consider one more example. Say we want to store photos of animals using Photo entity:

@Entity
public class Photo extends BaseEntity {
    @ManyToOne
    private Animal animal;

    private String photoFilename;

    // ...
}

For this class Hibernate will generate table:

CREATE TABLE photo
(
  id bigint NOT NULL,
  version bigint NOT NULL,
  photofilename character varying(255),
  animal_id bigint,

  CONSTRAINT photo_pkey PRIMARY KEY (id)
)

This time I used SQL instead of table picture to show an important fact. Do you see animal_id column in that table, it will be used to connect photos to animals. Unfortunately because various types of animals are stored in different tables we cannot create foreign key constraint on that column, this is serious drawback of table per concrete class strategy.

When we query for all photos with animals:

entityManager.createQuery(
        "select p from Photo p join fetch p.animal"
    ).getResultList()

Hibernate will execute this monstrous query:

select
    photo0_.id as id1_5_0_, animal1_.id as id1_1_1_, photo0_.version as version2_5_0_, photo0_.animal_id as animal_i4_5_0_, photo0_.photoFilename as photoFil3_5_0_, animal1_.version as version2_1_1_, animal1_.name as name3_1_1_, animal1_.isBarking as isBarkin1_2_1_, animal1_.isPurring as isPurrin1_0_1_, animal1_.grumpiness as grumpine1_3_1_, animal1_.sweetness as sweetnes1_4_1_, animal1_.clazz_ as clazz_1_ 
from
    Photo photo0_ 
inner join
    (
        select
            id, version, name, null::boolean as isBarking, isPurring, grumpiness, null::int4 as sweetness, 3 as clazz_ 
        from
            GrumpyCat 
        union
        all select
            id, version, name, null::boolean as isBarking, isPurring, null::int4 as grumpiness, sweetness, 4 as clazz_ 
        from
            Kitten 
        union
        all select
            id, version, name, isBarking, null::boolean as isPurring, null::int4 as grumpiness, null::int4 as sweetness, 1 as clazz_ 
        from
            Dog 
    ) animal1_ 
        on photo0_.animal_id=animal1_.id

I only add that complicated queries like this may cause serious performance problems.

To sum up here are pros and cons of table per concrete class strategy:

pros
Constraint friendly - You can introduce separate database constrains for each concrete subclass
Fast when querying concrete subclasses (queries directly access subclass table)
cons
May be slow when you query/lazy load abstract superclasses (union all and subquery)
Cannot introduce foreign key constrains for superclasses references (like in Photo example)

As you see when you are not using references to superclasses in your model this is strategy to go. When you have many references to superclasses it is better to use single table or table per every class strategies.

Table per every class

To demonstrate table per every class strategy we’ll use the same example that was used in the description of table per concrete class strategy: UML diagram of classes used in this example

Here are Java classes annotated to use table per every class strategy:

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class Animal extends BaseEntity {
    private String name;
    // ...
}

@Entity
@PrimaryKeyJoinColumn(name = "cat_id")
public abstract class AbstractCat extends Animal {
    private boolean isPurring;
    // ...
}

@Entity
public class GrumpyCat extends AbstractCat {
    private int grumpiness;
    // ...
}

@Entity
public class Kitten extends AbstractCat {
    public int sweetness;
    // ...
}

@Entity
@PrimaryKeyJoinColumn(name = "dog_id")
public class Dog extends Animal {
    private boolean isBarking;
    // ...
}

Notice that every class is marked as @Entity and root of inheritance hierarchy is annotated with @Inheritance(strategy = InheritanceType.JOINED). By default primary keys in tables corresponding to mapped classes will be named id. Sometimes it is useful to change name of the primary key column, we can use @PrimaryKeyJoinColumn(name = "primary_key_name") to provide new primary key column name.

For given example Hibernate will generate database schema: DB Schema generated for table per every class strategy Notice that Hibernate generated table per every class in the hierarchy. Lines between tables represent foreign key constrains inside database.

In table per every class strategy data from superclasses will be stored in dedicated tables, SQL joins will be used to gather entity data from all superclasses. When we add new entity to database Hibernate will split it according to inheritance hierarchy and will execute many inserts. An example will help us understand how it works.

First let’s try to insert some grumpy animals into database:

GrumpyCat cat = new GrumpyCat();
cat.setName("grumpy");
cat.setPurring(true);
cat.setGrumpiness(130);

entityManager.persist(cat);

To save single GrumpyCat entity to database Hibernate must generate three inserts:

insert into
    Animal
    (version, name, id) 
values
    (0, 'grumpy', 1)

insert into
    AbstractCat
    (isPurring, cat_id) 
values
    (true, 1)

insert into
    GrumpyCat
    (grumpiness, cat_id) 
values
    (130, 1)

Let’s see what will happen when we try to load all GrupyCats from database:

entityManager
        .createQuery("from GrumpyCat")
        .getResultList()

Hibernate will execute following SQL query:

select
    grumpycat0_.cat_id as id1_1_,
    grumpycat0_2_.version as version2_1_,
    grumpycat0_2_.name as name3_1_,
    grumpycat0_1_.isPurring as isPurrin1_0_,
    grumpycat0_.grumpiness as grumpine1_3_ 
from
    GrumpyCat grumpycat0_ 
inner join
    AbstractCat grumpycat0_1_ 
        on grumpycat0_.cat_id=grumpycat0_1_.cat_id 
inner join
    Animal grumpycat0_2_ 
        on grumpycat0_.cat_id=grumpycat0_2_.id

As we can see Hibernate used inner joins to gather GrumpyCat data that was split into GrumpyCat, AbstractCat and Animal tables. If we would query for Animals there would be ever more joins.

Now let’s see how storing/retrieving animal photos changed in table per every class strategy. We will use the same Photo class as in previous example:

@Entity
public class Photo extends BaseEntity {
    @ManyToOne
    private Animal animal;

    private String photoFilename;
    // ...
}

First we must notice that Photo table now has a foreign key constrain to Animal table:

CREATE TABLE photo
(
  id bigint NOT NULL,
  version bigint NOT NULL,
  photofilename character varying(255),
  animal_id bigint,
  CONSTRAINT photo_pkey PRIMARY KEY (id),

  CONSTRAINT fk6mbbc9717gifwpiqhd13t060r FOREIGN KEY (animal_id)
      REFERENCES animal (id) MATCH SIMPLE
)

When we try to query database for all photos with animals:

entityManager.createQuery(
        "select p from Photo p join fetch p.animal"
    ).getResultList()

Hibernate will execute query:

select
    photo0_.id as id1_5_0_, animal1_.id as id1_1_1_, photo0_.version as version2_5_0_, photo0_.animal_id as animal_i4_5_0_, photo0_.photoFilename as photoFil3_5_0_, animal1_.version as version2_1_1_, animal1_.name as name3_1_1_, animal1_1_.isBarking as isBarkin1_2_1_, animal1_2_.isPurring as isPurrin1_0_1_, animal1_3_.grumpiness as grumpine1_3_1_, animal1_4_.sweetness as sweetnes1_4_1_,
    case 
        when animal1_3_.cat_id is not null then 3 
        when animal1_4_.cat_id is not null then 4 
        when animal1_1_.dog_id is not null then 1 
        when animal1_2_.cat_id is not null then 2 
        when animal1_.id is not null then 0 
    end as clazz_1_ 
from
    Photo photo0_ 
inner join
    Animal animal1_ 
        on photo0_.animal_id=animal1_.id 
left outer join
    Dog animal1_1_ 
        on animal1_.id=animal1_1_.dog_id 
left outer join
    AbstractCat animal1_2_ 
        on animal1_.id=animal1_2_.cat_id 
left outer join
    GrumpyCat animal1_3_ 
        on animal1_.id=animal1_3_.cat_id 
left outer join
    Kitten animal1_4_ 
        on animal1_.id=animal1_4_.cat_id

This is really heavy query if plenty of joins, it may cause some performance problems.

Let’s end by presenting pros and cons of table per every class strategy:

pros
Constraint friendly - we may easily add constrains to database. References to superclasses are guarded by foreign key constrains.
cons
Poor performance - simple operations like saving entity to database or reading entity from database often require many SQL statements or complicated SQL queries with joins

Before using this strategy you should consider using simpler and faster table per hierarchy strategy. Use this strategy only if you have many subclasses that define many fields that cannot by shared using superclass.

The End

That was really long post, I hope it help you understand various inheritance strategies that we can use in Hibernate. As always with ORM’s the key to master this material is to spend few hours creating dummy models with mappings and checking what queries Hibernate generate. Party over meme

marcin-chwedczuk

A Programmer, A Geek, A Human