Mapping Inheritance Hierarchies in Hibernate with JPA Annotations

MappedSuperclass Strategy

The @MappedSuperclass annotation allows you to map common properties to a base class without creating a corresponding table for the base class itself. The parent class attributes are not persisted directly.

package com.example.entity;
import javax.persistence.MappedSuperclass;

@MappedSuperclass
public abstract class PaymentMethod {
    protected String holderName;

    public String getHolderName() {
        return holderName;
    }

    public void setHolderName(String holderName) {
        this.holderName = holderName;
    }
}

A concrete subclass mapping:

package com.example.entity;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class CreditCard extends PaymentMethod {
    @Id
    @GeneratedValue(generator="payment_generator")
    private Long id;
    private String cardNumber;
    private String expiryMonth;
    private String expiryYear;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getCardNumber() {
        return cardNumber;
    }

    public void setCardNumber(String cardNumber) {
        this.cardNumber = cardNumber;
    }

    public String getExpiryMonth() {
        return expiryMonth;
    }

    public void setExpiryMonth(String expiryMonth) {
        this.expiryMonth = expiryMonth;
    }

    public String getExpiryYear() {
        return expiryYear;
    }

    public void setExpiryYear(String expiryYear) {
        this.expiryYear = expiryYear;
    }
}

Another concrete subclass:

package com.example.entity;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class WireTransfer extends PaymentMethod {
    @Id
    @GeneratedValue(generator="payment_generator")
    private Long id;
    private String routingNumber;
    private String bankName;
    private String swiftCode;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getRoutingNumber() {
        return routingNumber;
    }

    public void setRoutingNumber(String routingNumber) {
        this.routingNumber = routingNumber;
    }

    public String getBankName() {
        return bankName;
    }

    public void setBankName(String bankName) {
        this.bankName = bankName;
    }

    public String getSwiftCode() {
        return swiftCode;
    }

    public void setSwiftCode(String swiftCode) {
        this.swiftCode = swiftCode;
    }
}

The generated table structure:

CREATE TABLE `creditcard` (
    `id` BIGINT(20) NOT NULL,
    `holdername` VARCHAR(255) NULL DEFAULT NULL,
    `cardnumber` VARCHAR(255) NULL DEFAULT NULL,
    `expirymonth` VARCHAR(255) NULL DEFAULT NULL,
    `expiryyear` VARCHAR(255) NULL DEFAULT NULL,
    PRIMARY KEY (`id`)
)
COLLATE='utf8mb4_general_ci'
ENGINE=InnoDB
;

CREATE TABLE `wiretransfer` (
    `id` BIGINT(20) NOT NULL,
    `holdername` VARCHAR(255) NULL DEFAULT NULL,
    `routingnumber` VARCHAR(255) NULL DEFAULT NULL,
    `bankname` VARCHAR(255) NULL DEFAULT NULL,
    `swiftcode` VARCHAR(255) NULL DEFAULT NULL,
    PRIMARY KEY (`id`)
)
COLLATE='utf8mb4_general_ci'
ENGINE=InnoDB
;

Table Per Concrete Class Strategy

Using @Inheritance(strategy=InheritanceType.TABLE_PER_CLASS) creates separate tables for each concrete entity. Hibernate unions the tables when querying the parent type.

package com.example.entity;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;

@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public abstract class PaymentMethod {
    @Id
    @GeneratedValue(generator="payment_generator")
    protected Long id;
    protected String holderName;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getHolderName() {
        return holderName;
    }

    public void setHolderName(String holderName) {
        this.holderName = holderName;
    }
}
package com.example.entity;
import javax.persistence.Entity;

@Entity
public class CreditCard extends PaymentMethod {
    private String cardNumber;
    private String expiryMonth;
    private String expiryYear;

    public String getCardNumber() {
        return cardNumber;
    }

    public void setCardNumber(String cardNumber) {
        this.cardNumber = cardNumber;
    }

    public String getExpiryMonth() {
        return expiryMonth;
    }

    public void setExpiryMonth(String expiryMonth) {
        this.expiryMonth = expiryMonth;
    }

    public String getExpiryYear() {
        return expiryYear;
    }

    public void setExpiryYear(String expiryYear) {
        this.expiryYear = expiryYear;
    }
}
package com.example.entity;
import javax.persistence.Entity;

@Entity
public class WireTransfer extends PaymentMethod {
    private String routingNumber;
    private String bankName;
    private String swiftCode;

    public String getRoutingNumber() {
        return routingNumber;
    }

    public void setRoutingNumber(String routingNumber) {
        this.routingNumber = routingNumber;
    }

    public String getBankName() {
        return bankName;
    }

    public void setBankName(String bankName) {
        this.bankName = bankName;
    }

    public String getSwiftCode() {
        return swiftCode;
    }

    public void setSwiftCode(String swiftCode) {
        this.swiftCode = swiftCode;
    }
}

Generated table structure:

CREATE TABLE `wiretransfer` (
    `id` BIGINT(20) NOT NULL,
    `holdername` VARCHAR(255) NULL DEFAULT NULL,
    `routingnumber` VARCHAR(255) NULL DEFAULT NULL,
    `bankname` VARCHAR(255) NULL DEFAULT NULL,
    `swiftcode` VARCHAR(255) NULL DEFAULT NULL,
    PRIMARY KEY (`id`)
)
COLLATE='utf8mb4_general_ci'
ENGINE=InnoDB
;

CREATE TABLE `creditcard` (
    `id` BIGINT(20) NOT NULL,
    `holdername` VARCHAR(255) NULL DEFAULT NULL,
    `cardnumber` VARCHAR(255) NULL DEFAULT NULL,
    `expirymonth` VARCHAR(255) NULL DEFAULT NULL,
    `expiryyear` VARCHAR(255) NULL DEFAULT NULL,
    PRIMARY KEY (`id`)
)
COLLATE='utf8mb4_general_ci'
ENGINE=InnoDB
;

Single Table Per Hierarchy Strategy

Using @Inheritance(strategy=InheritanceType.SINGLE_TABLE) maps the entire class hierarchy to a single database table. A discriminator column identifies the entity type. Use @DiscriminatorColumn to customize the column name (defaults to DTYPE), and @DiscriminatorValue to specify the value for each subclass.

package com.example.entity;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;

@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="payment_type")
public abstract class PaymentMethod {
    @Id
    @GeneratedValue(generator="payment_generator")
    protected Long id;
    protected String holderName;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getHolderName() {
        return holderName;
    }

    public void setHolderName(String holderName) {
        this.holderName = holderName;
    }
}
package com.example.entity;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;

@Entity
@DiscriminatorValue("CARD")
public class CreditCard extends PaymentMethod {
    protected String cardNumber;
    protected String expiryMonth;
    protected String expiryYear;

    public String getCardNumber() {
        return cardNumber;
    }

    public void setCardNumber(String cardNumber) {
        this.cardNumber = cardNumber;
    }

    public String getExpiryMonth() {
        return expiryMonth;
    }

    public void setExpiryMonth(String expiryMonth) {
        this.expiryMonth = expiryMonth;
    }

    public String getExpiryYear() {
        return expiryYear;
    }

    public void setExpiryYear(String expiryYear) {
        this.expiryYear = expiryYear;
    }
}
package com.example.entity;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;

@Entity
@DiscriminatorValue("WIRE")
public class WireTransfer extends PaymentMethod {
    protected String routingNumber;
    protected String bankName;
    protected String swiftCode;

    public String getRoutingNumber() {
        return routingNumber;
    }

    public void setRoutingNumber(String routingNumber) {
        this.routingNumber = routingNumber;
    }

    public String getBankName() {
        return bankName;
    }

    public void setBankName(String bankName) {
        this.bankName = bankName;
    }

    public String getSwiftCode() {
        return swiftCode;
    }

    public void setSwiftCode(String swiftCode) {
        this.swiftCode = swiftCode;
    }
}

Generated table structure:

CREATE TABLE `paymentmethod` (
    `payment_type` VARCHAR(31) NOT NULL,
    `id` BIGINT(20) NOT NULL,
    `holdername` VARCHAR(255) NULL DEFAULT NULL,
    `cardnumber` VARCHAR(255) NULL DEFAULT NULL,
    `expirymonth` VARCHAR(255) NULL DEFAULT NULL,
    `expiryyear` VARCHAR(255) NULL DEFAULT NULL,
    `routingnumber` VARCHAR(255) NULL DEFAULT NULL,
    `bankname` VARCHAR(255) NULL DEFAULT NULL,
    `swiftcode` VARCHAR(255) NULL DEFAULT NULL,
    PRIMARY KEY (`id`)
)
COLLATE='utf8mb4_general_ci'
ENGINE=InnoDB
;

Joined Subclass Strategy

Using @Inheritance(strategy=InheritanceType.JOINED) creates a separate table for each subclass. The parent table holds only the common fields, while subclass tables store subclass-specific attributes with a foreign key relationship to the parent table. The @PrimaryKeyJoinColumn annotation customizes the join column name; if omitted, Hibernate uses the parent's primary key by default.

package com.example.entity;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;

@Entity
@Inheritance(strategy=InheritanceType.JOINED)
public abstract class PaymentMethod {
    @Id
    @GeneratedValue(generator="payment_generator")
    protected Long id;
    protected String holderName;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getHolderName() {
        return holderName;
    }

    public void setHolderName(String holderName) {
        this.holderName = holderName;
    }
}
package com.example.entity;
import javax.persistence.Entity;
import javax.persistence.PrimaryKeyJoinColumn;

@Entity
@PrimaryKeyJoinColumn(name = "card_id")
public class CreditCard extends PaymentMethod {
    protected String cardNumber;
    protected String expiryMonth;
    protected String expiryYear;

    public String getCardNumber() {
        return cardNumber;
    }

    public void setCardNumber(String cardNumber) {
        this.cardNumber = cardNumber;
    }

    public String getExpiryMonth() {
        return expiryMonth;
    }

    public void setExpiryMonth(String expiryMonth) {
        this.expiryMonth = expiryMonth;
    }

    public String getExpiryYear() {
        return expiryYear;
    }

    public void setExpiryYear(String expiryYear) {
        this.expiryYear = expiryYear;
    }
}
package com.example.entity;
import javax.persistence.Entity;
import javax.persistence.PrimaryKeyJoinColumn;

@Entity
@PrimaryKeyJoinColumn(name = "transfer_id")
public class WireTransfer extends PaymentMethod {
    protected String routingNumber;
    protected String bankName;
    protected String swiftCode;

    public String getRoutingNumber() {
        return routingNumber;
    }

    public void setRoutingNumber(String routingNumber) {
        this.routingNumber = routingNumber;
    }

    public String getBankName() {
        return bankName;
    }

    public void setBankName(String bankName) {
        this.bankName = bankName;
    }

    public String getSwiftCode() {
        return swiftCode;
    }

    public void setSwiftCode(String swiftCode) {
        this.swiftCode = swiftCode;
    }
}

Tags: hibernate jpa Inheritance Mapping ORM java

Posted on Sun, 01 Nov 2026 16:27:10 +0000 by soshea