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;
}
}