Hibernate JPA Joined Table Inheritance Example

In the previous article, we have discussed a below two inheritance strategies:
In this article, we will discuss The Joined table strategy or table-per-subclass mapping strategy.
Each subclass can also be mapped to its own table. This is also called table-per-subclass mapping strategy. An inherited state is retrieved by joining with the table of the superclass.
A discriminator column is not required for this mapping strategy. Each subclass must, however, declare a table column holding the object identifier.
The JOINED table inheritance strategy addresses the data integrity concerns because every subclass is associated with a different table. Polymorphic queries or @OneToMany base class associations don’t perform very well with this strategy. However, polymorphic @ManyToOne associations are fine, and they can provide a lot of value.
Let's demonstrates SINGLE_TABLE inheritance strategy with a complete hibernate example.

Technologies and tools used

  • Hibernate 5.3.7.Final
  • IDE - Eclipse Noen
  • Maven 3.5.3
  • JavaSE 1.8
  • MySQL - 8.0.13

Development Steps

  1. Create a Simple Maven Project
  2. Project Directory Structure
  3. Add jar Dependencies to pom.xml
  4. Creating the JPA Entities
  5. Create a Hibernate configuration file - hibernate.cfg.xml
  6. Create a Hibernate utility class
  7. Create the Main class and Run an Application

1. Create a Simple Maven Project

Use How to Create a Simple Maven Project in Eclipse article to create simple Maven project in Eclipse IDE.

2. Project Directory Structure

The project directory structure for your reference - 

3. Add jar Dependencies to pom.xml

 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-core -->

4. Creating the JPA Entities

The inheritance strategy is defined on the abstract superclass, using the @Inheritance annotation. In this example, we used InheritanceType.JOINED. This means all concrete subclasses and superclass will be stored their own table. You can optionally specify a discriminator column name. This column is registered by the @DiscriminatorColumn if omitted no default is used.
Let's define the following Account base class:


package net.javaguides.hibernate.entity;

import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;

@Entity(name = "Account")
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class Account {

    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String owner;

    private double balance;

    private double interestRate;

    public Long getId() {
        return id;

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

    public String getOwner() {
        return owner;

    public void setOwner(String owner) {
        this.owner = owner;

    public double getBalance() {
        return balance;

    public void setBalance(double balance) {
        this.balance = balance;

    public double getInterestRate() {
        return interestRate;

    public void setInterestRate(double interestRate) {
        this.interestRate = interestRate;


The primary key of this table is also a foreign key to the superclass table and described by the @PrimaryKeyJoinColumns.
The table name still defaults to the non-qualified class name. Also, if @PrimaryKeyJoinColumn is not set, the primary key / foreign key columns are assumed to have the same names as the primary key columns of the primary table of the superclass.
Let's join table with @PrimaryKeyJoinColumn annotation:
package net.javaguides.hibernate.entity;

import javax.persistence.Entity;

@Entity(name = "CreditAccount")
@PrimaryKeyJoinColumn(name = "account_id")
public class CreditAccount extends Account {

    private double creditLimit;

    public double getCreditLimit() {
        return creditLimit;

    public void setCreditLimit(double creditLimit) {
        this.creditLimit = creditLimit;


Let's use @PrimaryKeyJoinColumn annotation to join the table.
package net.javaguides.hibernate.entity;

import javax.persistence.Entity;

@Entity(name = "DebitAccount")
@PrimaryKeyJoinColumn(name = "account_id")
public class DebitAccount extends Account {

    private double overdraftFee;

    public double getOverdraftFee() {
        return overdraftFee;

    public void setOverdraftFee(double overdraftFee) {
        this.overdraftFee = overdraftFee;

5. Create a Hibernate configuration file - hibernate.cfg.xml

The configuration file contains information about the database and mapping file. Conventionally, its name should be a hibernate.cfg.xml .
Let's create an XML file named as hibernate.cfg.xml under resources folder and write the following code in it.
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        <!-- JDBC Database connection settings -->
        <property name="connection.driver_class">com.mysql.cj.jdbc.Driver</property>
        <property name="connection.url">jdbc:mysql://localhost:3306/hibernate_db?useSSL=false</property>
        <property name="connection.username">root</property>
        <property name="connection.password">root</property>
        <!-- JDBC connection pool settings ... using built-in test pool -->
        <property name="connection.pool_size">1</property>
        <!-- Select our SQL dialect -->
        <property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>
        <!-- Echo the SQL to stdout -->
        <property name="show_sql">true</property>
        <!-- Set the current session context -->
        <property name="current_session_context_class">thread</property>
        <!-- Drop and re-create the database schema on startup -->
        <property name="hbm2ddl.auto">create-drop</property>
        <!-- dbcp connection pool configuration -->
        <property name="hibernate.dbcp.initialSize">5</property>
        <property name="hibernate.dbcp.maxTotal">20</property>
        <property name="hibernate.dbcp.maxIdle">10</property>
        <property name="hibernate.dbcp.minIdle">5</property>
        <property name="hibernate.dbcp.maxWaitMillis">-1</property>
        <mapping class="net.javaguides.hibernate.entity.CreditAccount" />
 <mapping class="net.javaguides.hibernate.entity.DebitAccount" />

6. Create a Hibernate utility Class

Create a helper class to bootstrap hibernate SessionFactory. In most Hibernate applications, the SessionFactory should be instantiated once during application initialization. The single instance should then be used by all code in a particular process, and any Session should be created using this single SessionFactory. The SessionFactory is thread-safe and can be shared; a Session is a single-threaded object.
The bootstrapping API is quite flexible, but in most cases, it makes the most sense to think of it as a 3 step process:
  1. Build the StandardServiceRegistry
  2. Build the Metadata
  3. Use those 2 to build the SessionFactory
package net.javaguides.hibernate.util;

import org.hibernate.SessionFactory;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;

public class HibernateUtil {
    private static StandardServiceRegistry registry;
    private static SessionFactory sessionFactory;

    public static SessionFactory getSessionFactory() {
        if (sessionFactory == null) {
            try {
                // Create registry
                registry = new StandardServiceRegistryBuilder().configure().build();

                // Create MetadataSources
                MetadataSources sources = new MetadataSources(registry);

                // Create Metadata
                Metadata metadata = sources.getMetadataBuilder().build();

                // Create SessionFactory
                sessionFactory = metadata.getSessionFactoryBuilder().build();

            } catch (Exception e) {
                if (registry != null) {
        return sessionFactory;

    public static void shutdown() {
        if (registry != null) {

7. Create the main App class and Run an Application

package net.javaguides.hibernate;

import org.hibernate.Session;
import org.hibernate.Transaction;

import net.javaguides.hibernate.entity.CreditAccount;
import net.javaguides.hibernate.entity.DebitAccount;
import net.javaguides.hibernate.util.HibernateUtil;

public class App {
    public static void main(String[] args) {

        Transaction transaction = null;
        try (Session session = HibernateUtil.getSessionFactory().openSession()) {
            // start a transaction
            transaction = session.beginTransaction();
            // save the account object
            CreditAccount account = new CreditAccount();

            DebitAccount debitAccount = new DebitAccount();
            // commit transaction

        try (Session session = HibernateUtil.getSessionFactory().openSession()) {
            // start a transaction
            Transaction transaction1 = session.beginTransaction();
            // save the account object
            CreditAccount creditAccount = session.get(CreditAccount.class, 1 L);
            // commit transaction
Since we used the JOINED inheritance strategy, all subclasses and superclass have their own table.
    balance NUMERIC(19, 2) ,
    interestRate NUMERIC(19, 2) ,
    owner VARCHAR(255) ,
    PRIMARY KEY ( id )

CREATE TABLE CreditAccount (
    creditLimit NUMERIC(19, 2) ,
    PRIMARY KEY ( id )

CREATE TABLE DebitAccount (
    overdraftFee NUMERIC(19, 2) ,
    PRIMARY KEY ( id )

ALTER TABLE CreditAccount
ADD CONSTRAINT FKihw8h3j1k0w31cnyu7jcl7n7n

ALTER TABLE DebitAccount
ADD CONSTRAINT FKia914478noepymc468kiaivqm