Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Inheritance

Relational tables know nothing about inheritance, but Java classes often form hierarchies. Suppose posts in the blog can have media attached: images with a width and height, and videos with a duration. Both are "media" with a title. Hibernate can store such a hierarchy in several ways — they are called inheritance strategies.

The hierarchy

// src/main/java/com/example/blog/Media.java
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class Media {
 
    @Id
    @GeneratedValue
    private Long id;
 
    private String title;
 
    protected Media() {
    }
 
    protected Media(String title) {
        this.title = title;
    }
 
    public String getTitle() {
        return title;
    }
 
    public abstract String describe();
}
// src/main/java/com/example/blog/Image.java
@Entity
public class Image extends Media {
 
    private int width;
    private int height;
 
    protected Image() {
    }
 
    public Image(String title, int width, int height) {
        super(title);
        this.width = width;
        this.height = height;
    }
 
    @Override
    public String describe() {
        return "image " + width + "x" + height;
    }
}

Video is written the same way, with an int seconds field. All three classes are listed in persistence.xml.

A polymorphic query

// src/main/java/com/example/blog/Main.java
emf.runInTransaction(em -> {
    em.persist(new Image("Cover", 1200, 630));
    em.persist(new Video("Demo", 95));
});
 
List<Media> media = emf.callInTransaction(em ->
        em.createQuery("select m from Media m order by m.id", Media.class).getResultList());
for (Media m : media) {
    IO.println(m.getTitle() + ": " + m.describe());
}

A query for Media returns objects of the right subclasses:

Hibernate: create table Media (height integer, seconds integer, width integer, id bigint not null, DTYPE varchar(31) not null check ((DTYPE in ('Image','Video'))), title varchar(255), primary key (id), ...)
Hibernate: insert into Media (title,height,width,DTYPE,id) values (?,?,?,'Image',?)
Hibernate: insert into Media (title,seconds,DTYPE,id) values (?,?,'Video',?)
Hibernate: select m1_0.id,m1_0.DTYPE,m1_0.title,m1_0.height,m1_0.width,m1_0.seconds from Media m1_0 order by m1_0.id
Cover: image 1200x630
Demo: video, 95 s

With SINGLE_TABLE the whole hierarchy lives in one table. The DTYPE column (discriminator) stores the class name, and columns of other subclasses stay empty. This is the default strategy and the fastest one: no joins.

JOINED

Replace the strategy with InheritanceType.JOINED, and each class gets its own table linked by the id:

Hibernate: create table Image (height integer not null, width integer not null, id bigint not null, primary key (id))
Hibernate: create table Media (id bigint not null, title varchar(255), primary key (id))
Hibernate: create table Video (seconds integer not null, id bigint not null, primary key (id))
Hibernate: insert into Media (title,id) values (?,?)
Hibernate: insert into Image (height,width,id) values (?,?,?)
Hibernate: select ... from Media m1_0 left join Image m1_1 on m1_0.id=m1_1.id left join Video m1_2 on m1_0.id=m1_2.id order by m1_0.id
StrategyTablesPros and cons
SINGLE_TABLEOneFast; subclass columns must allow null
JOINEDOne per classClean structure; every query joins tables
TABLE_PER_CLASSOne per concrete classPolymorphic queries use union; rarely used

Common fields without a hierarchy

Often you just want every entity to have the same id and creation date. That is what @MappedSuperclass is for:

// src/main/java/com/example/blog/BaseEntity.java
@MappedSuperclass
public abstract class BaseEntity {
 
    @Id
    @GeneratedValue
    private Long id;
 
    private LocalDateTime createdAt = LocalDateTime.now();
 
    public Long getId() {
        return id;
    }
}

A class Tag extends BaseEntity gets the table Tag (createdAt timestamp(6), id bigint not null, name varchar(255), ...). The superclass has no table of its own.

Common mistake

A @MappedSuperclass is not an entity, so it cannot be queried:

java.lang.IllegalArgumentException: org.hibernate.query.sqm.UnknownEntityException: Could not resolve root entity 'BaseEntity'

If you need queries across all subclasses, make the base class an @Entity with an inheritance strategy.

Thanks for reading!

Donate to the site — we work hard for you.