JPA + Hibernate @Inheritance - SINGLE_TABLE Example
December 11, 2023
On this page, we will learn to use JPA @Inheritance
annotation with SINGLE_TABLE strategy in our Hibernate application.
1. The
@Inheritance
annotation specifies the inheritance strategy to be used for an entity class hierarchy. The inheritance strategy can be JOINED, SINGLE_TABLE and TABLE_PER_CLASS.
2. In SINGLE_TABLE inheritance, more than one
@Entity
classes can persist data into single table of database. There will be more than one entity inheriting a root class and they will persist data in same table.
3. The
@Inheritance
annotation is specified at the entity class that is the root of the entity class hierarchy. If no @Inheritance
annotation is specified or no strategy is specified with @Inheritance
annotation, the default inheritance strategy is considered to be SINGLE_TABLE.
4. To use SINGLE_TABLE inheritance, we need to create a superclass (root class) and some subclasses. Super class is annotated with
@Entity
, @Inheritance
and optional @DiscriminatorColumn
annotation.
@Entity(name = "account") @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name="account_type") public class Account { ------ }
@Entity
and optional @DiscriminatorValue
annotation.
@Entity(name = "CreditAccount") @DiscriminatorValue(value = "Credit") public class CreditAccount extends Account { ------ }
7. The use of discriminator value is to discriminate the rows when we have multiple entity subclasses. Each subclass will persist data in same table with its own discriminator value.
@DiscriminatorColumn and @DiscriminatorValue
The@DiscriminatorColumn
and @DiscriminatorValue
are used to configure discriminator column name and its value to be inserted. In SINGLE_TABLE inheritance strategy, more than one entity persist data in the single table. Discriminator value is inserted to distinguish between rows inserted by different entities in the same table.
A. @DiscriminatorColumn :
1. The
@DiscriminatorColumn
specifies the discriminator column for the SINGLE_TABLE and JOINED Inheritance mapping strategies.
2. The
@DiscriminatorColumn
is used with @Inheritance
annotation at root entity.
@Entity(name = "account") @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name="account_type") public class Account { ------ }
account_type
in the account
table.
3. The
@DiscriminatorColumn
elements are columnDefinition, discriminatorType, length and name.
Example:
@DiscriminatorColumn( name="account_type", discriminatorType=DiscriminatorType.STRING, length=20 )
@DiscriminatorColumn
annotation is missing, then the default discriminator column is "DTYPE" .
B. @DiscriminatorValue :
1. The
@DiscriminatorValue
specifies the value of the discriminator column for entities of the given type. It can be applied only on the concrete entity class.
@Entity(name = "CreditAccount") @DiscriminatorValue(value = "Credit") public class CreditAccount extends Account { ------ }
2. If
@DiscriminatorValue
is not specified and discriminatorType is STRING, the default discriminator value is entity name.
@Entity(name = "CreditAccount") public class CreditAccount extends Account { ------ }
Complete Example
1. Technologies Used1. Java 19
2. Hibernate 6
3. Jakarta Persistence API 3
2. MySQL Database
In the SINGLE_TABLE inheritance strategy, more than one entity classes derived from a root class, persist data in a single table mapped with root class.
Find the table.
CREATE TABLE `account` ( `account_id` BIGINT(20) NOT NULL, `balance` DECIMAL(19,2) NULL DEFAULT NULL, `interestRate` DECIMAL(19,2) NULL DEFAULT NULL, `owner` VARCHAR(255) NULL DEFAULT NULL, `overdraftFee` DECIMAL(19,2) NULL DEFAULT NULL, `creditLimit` DECIMAL(19,2) NULL DEFAULT NULL, `account_type` VARCHAR(20) NOT NULL, PRIMARY KEY (`account_id`) )
account_type
column.
3. Java Code
Find the root class. Root class contains common fields for derived entity classes.
Account.java
@Entity(name = "account") @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name = "account_type", discriminatorType = DiscriminatorType.STRING, length = 20) public class Account { @Id @Column(name = "account_id") private Long id; @Column(name = "balance") private BigDecimal balance; @Column(name = "interestRate") private BigDecimal interestRate; @Column(name = "owner") private String owner; public Account(Long id, BigDecimal balance, BigDecimal interestRate, String owner) { this.id = id; this.balance = balance; this.interestRate = interestRate; this.owner = owner; } // Setters and Getters }
@Entity(name = "CreditAccount") @DiscriminatorValue(value = "Credit") public class CreditAccount extends Account { @Column(name = "creditLimit") private BigDecimal creditLimit; public CreditAccount(Long id, BigDecimal balance, BigDecimal interestRate, String owner, BigDecimal creditLimit) { super(id, balance, interestRate, owner); this.creditLimit = creditLimit; } // Setters and Getters }
CreditAccount
entity will persist data in in "account" table i.e. mapped with root class. The discriminator value "Credit" will be inserted in "account_type" column of "account" table.
Now find another derived entity.
DebitAccount.java
@Entity(name = "DebitAccount") @DiscriminatorValue(value = "Debit") public class DebitAccount extends Account { @Column(name = "overdraftFee") private BigDecimal overdraftFee; public DebitAccount(Long id, BigDecimal balance, BigDecimal interestRate, String owner, BigDecimal overdraftFee) { super(id, balance, interestRate, owner); this.overdraftFee = overdraftFee; } // Setters and Getters }
DebitAccount
entity will persist data in "account" table. The discriminator value "Debit" will be inserted in "account_type" column.
Now run the code.
HibernateUtil.java
public class HibernateUtil { private static final EntityManagerFactory emFactory; static { emFactory = Persistence.createEntityManagerFactory("com.concretepage"); } public static EntityManager getEntityManager(){ return emFactory.createEntityManager(); } }
public class Main { public static void main(String[] args) { EntityManager entityManager = HibernateUtil.getEntityManager(); entityManager.getTransaction().begin(); DebitAccount debitAccount = new DebitAccount(101L, BigDecimal.valueOf(150), BigDecimal.valueOf(1.2d), "Mahesh", BigDecimal.valueOf(50)); CreditAccount creditAccount = new CreditAccount(202L, BigDecimal.valueOf(1500), BigDecimal.valueOf(1.6d), "Krishn", BigDecimal.valueOf(1000)); entityManager.persist(debitAccount); entityManager.persist(creditAccount); entityManager.getTransaction().commit(); entityManager.close(); } }
Hibernate: insert into account (balance, interestRate, owner, overdraftFee, account_type, account_id) values (?, ?, ?, ?, 'Debit', ?) Hibernate: insert into account (balance, interestRate, owner, creditLimit, account_type, account_id) values (?, ?, ?, ?, 'Credit', ?)