Choosing a Strategy

Comparison

StrategyTablesPolymorphic queryNOT NULL on subclass fieldsBest when
SINGLE_TABLE1fastest (no joins)nofew subclass fields, frequent polymorphic queries (default choice)
JOINED1 + one per subclassjoinsyesmany subclass fields, strict constraints
TABLE_PER_CLASSone per concrete classUNION ALLyesrare polymorphic queries
@MappedSuperclassone per entitynot possibleyesshared technical fields only

Decision Flow

Do you need to query or reference the PARENT type (Payment)?
   ├── no  ──► @MappedSuperclass
   └── yes ──► Do subclasses add only a few columns?
                 ├── yes ──► SINGLE_TABLE  (+ CHECK constraints / validation)
                 └── no  ──► Need NOT NULL & normalized data, can afford joins?
                               ├── yes ──► JOINED
                               └── no  ──► TABLE_PER_CLASS

Tip: Do not reach for inheritance by default. A single Payment entity with a method enum and a JSON details column is often simpler than any hierarchy. Prefer composition (embeddables) when the "is-a" relationship is weak.

Practice

Exercise: ShopEase adds notifications: EmailNotification(to, subject), SmsNotification(phone), PushNotification(deviceToken), all with message and sentAt. The admin page lists all notifications newest first. Which strategy, and how do you map it?

Show answer
// Few extra columns + a polymorphic list page -> SINGLE_TABLE
@Entity
@Table(name = "notifications")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "channel")
public abstract class Notification extends BaseEntity {
    private String message;
    private Instant sentAt;
}

@Entity @DiscriminatorValue("EMAIL") public class EmailNotification extends Notification { private String toAddress; private String subject; }
@Entity @DiscriminatorValue("SMS")   public class SmsNotification   extends Notification { private String phone; }
@Entity @DiscriminatorValue("PUSH")  public class PushNotification  extends Notification { private String deviceToken; }

// one query, no joins:
Page<Notification> findAllByOrderBySentAtDesc(Pageable pageable);