JPA + Hibernate @Inheritance - SINGLE_TABLE Example

By Arvind Rai, 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 {
   ------
} 
5. Subclasses are annotated with @Entity and optional @DiscriminatorValue annotation.
@Entity(name = "CreditAccount")
@DiscriminatorValue(value = "Credit")
public class CreditAccount extends Account {
------
} 
6. Subclass inherits all the mappings of superclass. When we persist the instance of subclass, data is inserted in the table mapped with superclass entity with given discriminator value.
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 {
------
} 
In the above code snippet, the discriminator column is 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
) 
4. If @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 {
  ------
} 
The discriminator value will be inserted as 'Credit'.
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 {
  ------
} 
Here discriminator value will be inserted as 'CreditAccount'.

Complete Example

1. Technologies Used
1. 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`)
) 
Discriminator value will be inserted in 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
} 
CreditAccount.java
@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
} 
In the above entity, entity name "CreditAccount" is not representing any table. The 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
} 
In the above entity, entity name "DebitAccount" is not representing any table. The 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();
	}
}
Main.java
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();
  }
} 
Output
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', ?) 
Find the data inserted into database.
JPA + Hibernate @Inheritance - SINGLE_TABLE Example

References

Download Source Code

POSTED BY
ARVIND RAI
ARVIND RAI







©2024 concretepage.com | Privacy Policy | Contact Us