liveBook · Manning

Table-per-class inheritance strategy

As explained in Java Persistence with Spring Data and Hibernate epub

Table-per-concrete-class mapping stores the persistent state of each non-abstract class in a separate relational table.

Table-per-class mapping gives each concrete entity class its own table. A subclass table holds both its own persistent properties and those inherited from its superclass. This represents a Java inheritance hierarchy without introducing inheritance relationships between SQL tables; shared columns are duplicated. section 7.0 section 7.2

For example, an abstract BillingDetails entity can have CreditCard and BankAccount subclasses stored in CREDITCARD and BANKACCOUNT. Declare @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) on BillingDetails and @Entity on each subclass. Put the identifier mapping on the superclass so every subclass table inherits it and has an ID primary key. If BillingDetails were concrete, it would also need a table. section 7.2

Listing 7.8 Mapping BillingDetails with TABLE_PER_CLASS
Path: Ch07/mapping-inheritance-tableperclass/src/main/java/com/manning
 /javapersistence/ch07/model/BillingDetails.java
 
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class BillingDetails {
    @Id
    @GeneratedValue(generator = “ID_GENERATOR”)
    private Long id;
    @NotNull
    private String owner;
    //  . . .
}

The superclass declaration places the inheritance strategy, generated identifier, and shared owner property in one mapping. section 7.2

The explicit TABLE_PER_CLASS strategy preserves polymorphic queries. A repository query for all BillingDetails, or the JPQL query `select bd from BillingDetails bd`, reads across concrete tables. The generated SQL combines their rows with UNION ALL, fills columns absent from a particular subtype with null, and includes a literal identifying each concrete class. A superclass-targeting repository can participate once BillingDetails is an entity and its repository is no longer marked @NoRepositoryBean. section 7.2

select
    ID, OWNER, EXPMONTH, EXPYEAR, CARDNUMBER,
    ACCOUNT, BANKNAME, SWIFT, CLAZZ_
 from
    ( select
          ID, OWNER, EXPMONTH, EXPYEAR, CARDNUMBER,
          null as ACCOUNT,
          null as BANKNAME,
          null as SWIFT,
          1 as CLAZZ_
      from
          CREDITCARD
      union all
      select
          id, OWNER,
          null as EXPMONTH,
          null as EXPYEAR,
          null as CARDNUMBER,
          ACCOUNT, BANKNAME, SWIFT,
          2 as CLAZZ_
      from
          BANKACCOUNT
    ) as BILLINGDETAILS

The generated query shows how separate subtype tables supply one polymorphic result, including null placeholders and class-identifying literals. section 7.2

Choose the variant and its tradeoffs deliberately:

  • Implicit table-per-concrete-class mapping is simplest when superclass queries and associations are unnecessary, but it supports polymorphic associations poorly. Duplicating columns with the same meaning also complicates schema evolution. section 7.7 section 7.9
  • Prefer explicit TABLE_PER_CLASS when polymorphic queries or associations may be needed later. JPA support for this strategy is optional, so check the implementation. section 7.7 section 7.2
  • For frequent polymorphic access, compare SINGLE_TABLE and JOINED against TABLE_PER_CLASS. Subtype attributes, nullable-column requirements, and the cost of joins versus unions affect the choice; complex cases may warrant SQL execution plans with real data. section 7.7

A polymorphic one-to-many collection can also target the superclass: Hibernate uses the appropriate SQL, including unions for TABLE_PER_CLASS, when loading its elements. The target superclass must be mapped as @Entity with @Inheritance, not merely as @MappedSuperclass. section 7.8.2

How Java Persistence with Spring Data and Hibernate epub covers it

How deep it goes
Works through entity annotations, inherited identifiers, repository queries, and generated UNION SQL, then weighs selection and schema-evolution costs.
Examples
Uses a BillingDetails hierarchy with CreditCard and BankAccount tables, a Spring Data JPA repository, and generated UNION SQL.
What it assumes
Assumes familiarity with Java entity classes and SQL tables while introducing the inheritance annotation and its query behavior.
What you can do after
Apply it via JPA TABLE_PER_CLASS annotations and assess when unions justify separate concrete tables.
How it connects
Connects inheritance mapping to polymorphic queries, one-to-many associations, SINGLE_TABLE, JOINED, and schema evolution.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Java Persistence with Spring Data and Hibernate epub. The section links point to the parts of the book it draws on.