Inheritance — Table per Class Strategy
IntermediateEach concrete subclass gets its own table with all inherited columns repeated; polymorphic queries use UNION which can be expensive; rarely recommended.
Overview
TABLE_PER_CLASS maps each concrete subclass to its own table that contains all inherited columns plus the subclass-specific ones — no base table exists. There is no join required for single-subclass queries, but polymorphic queries (querying the base class) generate a UNION ALL across all subclass tables, which is expensive. The IDENTITY id generation strategy cannot be used because IDs must be unique across all tables (each table starts its own auto-increment). Use TABLE or SEQUENCE strategies instead. TABLE_PER_CLASS is rarely the best choice; consider JOINED for normalisation or SINGLE_TABLE for performance.
Declaring Table-Per-Class
Annotate the base class with @Inheritance(strategy=TABLE_PER_CLASS). No @DiscriminatorColumn is needed. Each concrete subclass gets its own table with all inherited + own columns. The base class itself has no table.
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Notification {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE) // IDENTITY not allowed — must be SEQUENCE or TABLE
private Long id;
@Column(nullable = false)
private String recipient;
private LocalDateTime sentAt;
}
@Entity
@Table(name = "email_notifications") // contains: id, recipient, sent_at, subject, body
public class EmailNotification extends Notification {
private String subject;
private String body;
}
@Entity
@Table(name = "sms_notifications") // contains: id, recipient, sent_at, phone_number, message
public class SmsNotification extends Notification {
private String phoneNumber;
private String message;
}Polymorphic Query — UNION ALL
Querying the base class generates UNION ALL across all concrete tables. For two subclasses this means two SELECTs merged; for many subclasses this becomes very expensive.
// JPQL — polymorphic base query
List<Notification> all = em.createQuery(
"SELECT n FROM Notification n", Notification.class).getResultList();
// Generated SQL (two subclasses):
// SELECT id, recipient, sent_at, subject, body, NULL as phone_number, NULL as message,
// 'EMAIL' as clazz_ FROM email_notifications
// UNION ALL
// SELECT id, recipient, sent_at, NULL as subject, NULL as body, phone_number, message,
// 'SMS' as clazz_ FROM sms_notifications
// Single-subclass query — no UNION, fast
List<EmailNotification> emails = em.createQuery(
"SELECT e FROM EmailNotification e", EmailNotification.class).getResultList();Why TABLE_PER_CLASS is Rarely Chosen
The combination of column duplication and UNION ALL polymorphic queries makes TABLE_PER_CLASS the least recommended strategy. Use it only when you never query the base class polymorphically.
/*
* Use TABLE_PER_CLASS only when:
* 1. You never (or very rarely) query the base class
* 2. You want NOT NULL on subclass columns
* 3. You don't want a join table (as in JOINED strategy)
*
* Prefer alternatives:
* - JOINED → normalised schema, moderate join cost
* - SINGLE_TABLE → best performance for polymorphic queries, nullable subclass columns
*
* ID strategy constraint:
* IDENTITY (auto_increment) cannot guarantee uniqueness across tables
* → must use SEQUENCE or TABLE generator
*/
@GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "notification_seq")
@SequenceGenerator(name = "notification_seq",
sequenceName = "notification_id_seq",
allocationSize = 50)Key Points to Remember
- 1Each concrete subclass gets its own table; abstract base class has no table
- 2Inherited columns are duplicated in every subclass table — no base table to join
- 3Polymorphic queries on the base class generate UNION ALL — expensive with many subclasses
- 4IDENTITY strategy is incompatible — use SEQUENCE or TABLE for unique IDs across tables
- 5Single-subclass queries are fast (no join, no union); only polymorphic queries are costly
- 6Rarely recommended — prefer JOINED for normalisation or SINGLE_TABLE for polymorphic perf
Interview Questions
Sign in to ask AriaWhat SQL does Hibernate generate for a polymorphic TABLE_PER_CLASS query?
Why can't you use @GeneratedValue(strategy=IDENTITY) with TABLE_PER_CLASS?
In what scenario would TABLE_PER_CLASS outperform JOINED strategy?
What is the key difference between TABLE_PER_CLASS and JOINED strategies?
Does the abstract base class get its own table in TABLE_PER_CLASS?
Ask Aria about Inheritance — Table per Class Strategy
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.