Single-table inheritance stores every class in an entity hierarchy in one table. A discriminator identifies each row’s concrete type, while shared and subtype-specific fields occupy columns in that table. It is a practical fit when code needs polymorphic queries or associations and subclasses add relatively few fields. section 7.3 section 7.7
Listing 7.12 Mapping BillingDetails with SINGLE_TABLE
Path: Ch07/mapping-inheritance-singletable/src/main/java/com/manning /javapersistence/ch07/model/BillingDetails.java @Entity @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name = "BD_TYPE") public abstract class BillingDetails { @Id @GeneratedValue(generator = "ID_GENERATOR") private Long id; @NotNull @Column(nullable = false) private String owner; // . . . }
Map the root entity with @Inheritance(strategy = InheritanceType.SINGLE_TABLE). @DiscriminatorColumn names the type column; @Column(nullable = false) makes the shared owner field non-null in the generated schema. section 7.3
Listing 7.13 Mapping CreditCard using the SINGLE_TABLE inheritance strategy
Path: Ch07/mapping-inheritance-singletable/src/main/java/com/manning /javapersistence/ch07/model/CreditCard.java @Entity @DiscriminatorValue("CC") public class CreditCard extends BillingDetails { @NotNull private String cardNumber; @NotNull private String expMonth; @NotNull private String expYear; // . . . }
Map a subclass as an entity and give it an explicit discriminator value. Hibernate uses that value to distinguish its rows from other billing details. section 7.3
Polymorphic and subtype queries
With CreditCard and BankAccount rows in BILLINGDETAILS, a query for BillingDetails reads the hierarchy table and its discriminator. A query for CreditCard adds a BD_TYPE='CC' restriction. Hibernate sets and retrieves discriminator values automatically; the discriminator is not an entity property. section 7.3
The compact schema has a cost: columns used only by one subtype must allow NULL for rows of other types. That prevents a table-level NOT NULL constraint from guaranteeing a required credit-card expiration date. Shared columns can still be NOT NULL. This design also introduces functional dependencies between non-key columns, so weigh apparent performance gains against normalization and data integrity rather than assuming one table is faster. section 7.3
Specify discriminator values explicitly rather than relying on provider-dependent defaults. Without an explicit discriminator column, the default column name is DTYPE. For a schema that lacks a discriminator column, Hibernate also offers @DiscriminatorFormula; one example distinguishes credit cards by whether CARDNUMBER is non-null. section 7.3
A hierarchy uses one inheritance strategy; it cannot simply switch a subclass to JOINED. A targeted workaround is @SecondaryTable: move CreditCard fields to a table keyed to the hierarchy table and map them with @Column(table = "CREDITCARD"). Those fields can then be NOT NULL, but polymorphic loading uses an outer join. Prefer SINGLE_TABLE for simple hierarchies with few nullable subtype columns; consider JOINED when subclasses carry many required fields or normalization and constraints take priority. TABLE_PER_CLASS is another option whose joins-versus-unions tradeoff may require SQL-plan evaluation with real data. section 7.5 section 7.7
A polymorphic one-to-many collection can also target the hierarchy root. That root must be an entity with an inheritance mapping, not a @MappedSuperclass. section 7.8.2
