BackendBench
Coffee

Unravel โ€บ Java concepts โ€บ Persistence and JPA

Persistence

JPA inheritance mapping

Object models have inheritance (a Payment is a CardPayment or a BankTransfer) but tables do not.

Learn these first

At a glance

Mental model

Storing different vehicle types: one big register with a column "type" (single table), a general register plus one extra sheet per type (joined), or a completely separate register per type (table per class).

What happens, in order
  1. 1SINGLE_TABLE (default): all subclasses in one table with a discriminator column and nullable columns for subclass fieldsFast, no joins, but subclass columns cannot be NOT NULL.
  2. 2JOINED: a base table plus one table per subclass joined by primary keyNormalised and constraint friendly, but every load joins tables.
  3. 3TABLE_PER_CLASS: one complete table per concrete classPolymorphic queries become UNIONs, so it is rarely a good choice.
  4. 4@MappedSuperclass is different: it shares fields (id, audit columns) between entities but is not itself an entity and cannot be queried
Remember
Watch out
Choosing TABLE_PER_CLASS and then querying the base typeDeep inheritance trees instead of compositionCasting a lazy proxy to a subclassExpecting @MappedSuperclass to be queryable
Example (highlighted lines explain it)
@Entity@Inheritance(strategy = InheritanceType.SINGLE_TABLE)@DiscriminatorColumn(name = "kind")public abstract class Payment {    @Id @GeneratedValue    private Long id;    private BigDecimal amount;}@Entity@DiscriminatorValue("CARD")public class CardPayment extends Payment {    private String last4;}
  1. line 2 uses Inheritance, the core of JPA inheritance mapping
  2. line 3 uses DiscriminatorColumn, the core of JPA inheritance mapping
  3. line 11 uses DiscriminatorValue, the core of JPA inheritance mapping
Read the full explanation (about 2 min)

Object models have inheritance (a Payment is a CardPayment or a BankTransfer) but tables do not. JPA offers three ways to store a class hierarchy, each trading speed against clean schemas.

A mental model

Storing different vehicle types: one big register with a column "type" (single table), a general register plus one extra sheet per type (joined), or a completely separate register per type (table per class).

How it works

SINGLE_TABLE (default): all subclasses in one table with a discriminator column and nullable columns for subclass fields. Fast, no joins, but subclass columns cannot be NOT NULL.

JOINED: a base table plus one table per subclass joined by primary key. Normalised and constraint friendly, but every load joins tables.

TABLE_PER_CLASS: one complete table per concrete class. Polymorphic queries become UNIONs, so it is rarely a good choice.

@MappedSuperclass is different: it shares fields (id, audit columns) between entities but is not itself an entity and cannot be queried.

Going deeper

Polymorphic queries (from Payment) return all subtypes, which can fetch far more than needed. Query the concrete subclass when you can.

Deep hierarchies hurt. Often composition (a Payment with a PaymentMethod value) is simpler than inheritance and avoids the whole mapping question.

With lazy loading, a proxy of the base class is not an instance of the subclass, so instanceof and casts can fail. Use a visitor, a type field, or Hibernate.unproxy.

Under the hood

Hibernate reads the discriminator value (or the presence of a row in the subclass table) to decide which class to instantiate for each row. For SINGLE_TABLE it adds a where dtype in (...) filter when you query a subclass; for JOINED it uses left joins for polymorphic loads and inner joins for concrete ones.

Common mistakes

  • Choosing TABLE_PER_CLASS and then querying the base type.
  • Deep inheritance trees instead of composition.
  • Casting a lazy proxy to a subclass.
  • Expecting @MappedSuperclass to be queryable.

How Unravel recognises it

When you paste code, Unravel looks for these signs of jpa inheritance mapping. Spotting them yourself is a good habit when you read unfamiliar code:

Part of a learning path

Related concepts

Frequently asked questions

What is JPA inheritance mapping?

Object models have inheritance (a Payment is a CardPayment or a BankTransfer) but tables do not. JPA offers three ways to store a class hierarchy, each trading speed against clean schemas.

What are the most common mistakes with JPA inheritance mapping?

Choosing TABLE_PER_CLASS and then querying the base type. Deep inheritance trees instead of composition. Casting a lazy proxy to a subclass. Expecting @MappedSuperclass to be queryable.

How can I see JPA inheritance mapping in real code?

Paste your own Java into Unravel and it will point out where jpa inheritance mapping appears, explain how it works at the detail level you choose, and draw the flow of the code around it.