Dev Learning Lab

The labs on this site need JavaScript to run. The same content is written out below.

Learn › JPA › Relationships

Inheritance mapping

In how many tables, and how, are inherited classes stored?

Java has inheritance, but the tables of a relational database do not. So you have to choose how to store a superclass Item and its subclasses Book, Album, and Movie in tables. JPA has three strategies, and the one you choose changes the number and shape of the tables, the number of INSERTs when you save, and the shape of the queries when you load.

Checked against real execution · 27 combinations were run on Hibernate 7.4.5 and H2, and the tables, the number of INSERTs, the numbers of joins and UNIONs in the find queries, and the rows left were checked

Concepts in this lab

Inheritance mapping
The choice of how to store the inheritance structure of Java classes in tables. A relational database has no inheritance, so you have to choose which tables the fields of a superclass and its subclasses go into. In JPA, you put @Inheritance on the root entity and specify one of three strategies: joined subclass, single table, and table per concrete class. If you do not specify one, the single table strategy is used. The strategy changes the number of tables, the number of INSERTs when you save, and the shape of the query when you load.
Joined subclass strategy (JOINED)
An inheritance mapping strategy that creates a separate table for the superclass and for each subclass, and makes the primary key of a subclass table a foreign key that points to the primary key of the superclass table. The fields of the superclass are stored in the superclass table and the fields that only the subclass has are stored in the subclass table, so no value is repeated and no empty cells appear. In exchange, saving one object sends two INSERTs, and loading requires joins. If you find by the superclass type, it cannot tell which subclass the row belongs to, so it joins all the subclass tables.
Single table per class hierarchy strategy (SINGLE_TABLE)
An inheritance mapping strategy that puts the fields of the superclass and all the subclasses into one table. A discriminator column (named DTYPE by default) is created along with it, and it records which subclass each row belongs to. No join is needed, so the queries for saving and loading are simple. In exchange, the columns of the other subclasses are left as null, so the columns of the subclasses must allow null, and the table gets wider as subclasses are added.
Table per concrete class strategy (TABLE_PER_CLASS)
An inheritance mapping strategy that has no superclass table and creates one table for each subclass, repeating the superclass fields in every subclass table. It is simple when you save and load just one subclass type, because you need only that one table. But if you find by the superclass type, it cannot tell which table has the row, so it has to combine all the subclass tables with UNION. That is why this strategy is best avoided when you need to work with objects through the superclass type.
JOIN (INNER, OUTER)
The operation that combines rows of two tables according to a condition. INNER JOIN includes only the rows where the condition matches on both sides. LEFT OUTER JOIN includes every row of the left table and fills in NULL when there is no match on the right. Choose between them based on whether data without a match must stay in the result.

Try these

Running with the default settings

  1. new Book(1L, "Java", 20000, "Alice"): Creates one Book object. The id, name, and price inherited from the superclass Item and the author declared by Book are all together in this one object.
  2. em.persist(book): Hands the object to the persistence context. You set the id in the code yourself, so there is nothing to ask the DB, and the INSERT is not sent right away. It is postponed until commit.
  3. Transaction commit, first INSERT: With the joined subclass strategy, the fields of the superclass (id, name, price) go into the superclass table item. First, one row is inserted into item.
  4. Second INSERT: The field that only Book has (author) goes into the subclass table book. The id of book is both its primary key and a foreign key that points to the id of item. Saving one object took 2 INSERTs.

Result of this run

Code

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
abstract class Item {
    @Id
    Long id;
    String name;
    int price;
}

@Entity
class Book extends Item {
    String author;
}

@Entity
class Album extends Item {
    String artist;
}

@Entity
class Movie extends Item {
    String actor;
}
// Constructors are omitted

// Inside a transaction
Book book = new Book(1L, "Java", 20000, "Alice");
em.persist(book);
// Transaction commit

Assumptions of this model

This lab assumes that the superclass Item is an abstract class and that each subclass has only one field. The primary key is set directly in the code, because the IDENTITY strategy cannot be used with the table per concrete class strategy (it was confirmed that Hibernate 7.4.5 throws a MappingException at startup). All 27 combinations (3 strategies, 3 things to do, and 1, 2, or 3 subclasses) were run on Hibernate 7.4.5 and an H2 database with code of the same structure, and the following were confirmed to match the screen: the tables and columns that were created, the foreign keys from the subclass tables to the superclass table, the kinds of SQL sent and how many times for each table, the tables read by the find query and the number of joins and UNIONs, whether a discriminator column condition is added, the type of the returned object, and the rows left in the DB. With the joined subclass strategy and the table per concrete class strategy, Hibernate also creates temporary tables (their names start with ht_ or hte_) for bulk update queries. They have nothing to do with the mapping, so they are left out of the screen. That the joined subclass strategy has no discriminator column (DTYPE) is Hibernate's default behavior, and it was also confirmed by running that adding @DiscriminatorColumn creates a dtype column in the item table. It was also confirmed that if you do not write @Inheritance at all, the same table as the single table strategy is created. The SQL statements on the screen are shortened to be easy to read, so they are not letter-for-letter the same as the actual statements. In the actual SQL, a find by the superclass type tells the subclasses apart with a CASE expression in the joined subclass strategy, and with a number attached to each subclass in the table per concrete class strategy. The order of the columns inside a table may also differ from the actual one.

Previous lab: Many-to-one, one-to-many, one-to-one, many-to-many · Next lab: Cascade and orphan removal

Concepts in this lab

What this lab shows

Settings

Code


      
    

Diagram

Run comparison

Try these