JPA + Hibernate @Inheritance - SINGLE_TABLE Example
February 25, 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
4. MySQL 5.5
2. pom.xml
<dependencies> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>6.1.6.Final</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>6.0.0.Alpha7</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.32</version> </dependency> </dependencies>
<?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="https://jakarta.ee/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd" version="3.0"> <persistence-unit name="com.concretepage"> <description>Procedure Demo</description> <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider> <properties> <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" /> <property name="jakarta.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver" /> <property name="jakarta.persistence.jdbc.url" value="jdbc:mysql://localhost/concretepage" /> <property name="jakarta.persistence.jdbc.user" value="root" /> <property name="jakarta.persistence.jdbc.password" value="cp" /> <property name="hibernate.show_sql" value="true" /> </properties> </persistence-unit> </persistence>
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.
5. Java Code
Find the root class. Root class contains common fields for derived entity classes.
Account.java
package com.concretepage.entity; import java.math.BigDecimal; import jakarta.persistence.Column; import jakarta.persistence.DiscriminatorColumn; import jakarta.persistence.DiscriminatorType; import jakarta.persistence.Entity; import jakarta.persistence.Id; import jakarta.persistence.Inheritance; import jakarta.persistence.InheritanceType; @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 }
package com.concretepage.entity; import java.math.BigDecimal; import jakarta.persistence.Column; import jakarta.persistence.DiscriminatorValue; import jakarta.persistence.Entity; @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
package com.concretepage.entity; import java.math.BigDecimal; import jakarta.persistence.Column; import jakarta.persistence.DiscriminatorValue; import jakarta.persistence.Entity; @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
package com.concretepage.util; import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.Persistence; public class HibernateUtil { private static final EntityManagerFactory emFactory; static { emFactory = Persistence.createEntityManagerFactory("com.concretepage"); } public static EntityManager getEntityManager(){ return emFactory.createEntityManager(); } }
package com.concretepage; import java.math.BigDecimal; import com.concretepage.entity.CreditAccount; import com.concretepage.entity.DebitAccount; import com.concretepage.util.HibernateUtil; import jakarta.persistence.EntityManager; 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', ?)
