Hibernate Inheritance strategy: Table Per SubClass Hierarchy

Introduction

In Table Per SubClass Hierarchy there will be the number of classes equals to the number of tables in the database. If you save the Student class object, hibernate will first save the data related to super class object into the super class related table in the database and then Student object data in Student related table in the database.

So in this example I am going to create separate table for Person, Student and Teacher classes.

In the hibernate mapping file, <key/> tag or annotation @PrimaryKeyJoinColumn is used, because once you save the derived class object, hibernate will first save the base class object then derived class object. So at the time of saving the derived class object hibernate will copy the primary key value of the base class into the corresponding derived class.

hibernate inheritance strategy

In the above hierarchy, three classes are involved where Person is the super class and Student and Teacher are subclasses with their own properties declared as instance variables.

Related Posts:

Prerequisites

Java at least 8, Maven 3.6.3, Hibernate 6.0.0.Alpha8, MySQL 8.0.22

Project Setup

Create a maven based project in your favorite IDE or tool. The name of the project is hibernate-inheritance-table-per-subclass.

The required dependencies in pom.xml file can be given as follows:

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.roytuts</groupId>
	<artifactId>hibernate-inheritance-table-per-subclass</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<maven.compiler.source>12</maven.compiler.source>
		<maven.compiler.target>12</maven.compiler.target>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-core</artifactId>
			<version>6.0.0.Alpha8</version>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>8.0.22</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.8.1</version>
			</plugin>
		</plugins>
	</build>
</project>

MySQL Table

As the table per sub-class inheritance hierarchy requires a separate table for each class so I am going to create three tables in MySQL database.

CREATE TABLE person (
    id int unsigned COLLATE utf8mb4_unicode_ci,
	name varchar(50) COLLATE utf8mb4_unicode_ci,   
    PRIMARY KEY (id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE student (
	skey int unsigned COLLATE utf8mb4_unicode_ci,
	year varchar(50) COLLATE utf8mb4_unicode_ci
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE teacher (
	tkey int unsigned COLLATE utf8mb4_unicode_ci,
	subject varchar(50) COLLATE utf8mb4_unicode_ci
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

The skey and tkey columns in the respective tables are primary id values from person table.

Entity Classes

Here I am going to create the base entity class Person that will be extended by two sub-classes Student and Teacher and these sub-classes will inherit properties from the base class.

Person

The base class I am going to create as an abstract class. I am not using auto-increment value for id field so I am not using generation strategy for it.

I am specifying the inheritance strategy using annotation @Inheritance.

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

	@Id
	// @GeneratedValue(strategy = GenerationType.IDENTITY)
	private int id;

	@Column(name = "name")
	private String name;

	public Person() {
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

}

Student

The sub-class Student has the following signature. In this sub-class I have specified the table name as I am creating separate table for each class. I have specified the key column name using annotation @PrimaryKeyJoinColumn.

@Entity
@Table(name = "student")
@PrimaryKeyJoinColumn(name = "skey")
public class Student extends Person {

	@Column(name = "year")
	private String year;

	public String getYear() {
		return year;
	}

	public void setYear(String year) {
		this.year = year;
	}

}

Teacher

The Teacher class has the following details with the key column name specified.

@Entity
@Table(name = "teacher")
@PrimaryKeyJoinColumn(name = "tkey")
public class Teacher extends Person {

	@Column(name = "subject")
	private String subject;

	public String getSubject() {
		return subject;
	}

	public void setSubject(String subject) {
		this.subject = subject;
	}

}

Hibernate SessionFactory

The singleton SessionFactory class is needed to get non-thread safe session object from it. Here I am using annotation based configuration for building the session factory and mapping the entity classes.

public final class HibernateUtil {

	private HibernateUtil() {
	}

	private static SessionFactory sessionFactory;

	public static SessionFactory getSessionFactory() {

		if (sessionFactory == null) {
			try {
				Configuration configuration = configuration();
				ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
						.applySettings(configuration.getProperties()).build();

				sessionFactory = configuration.buildSessionFactory(serviceRegistry);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		return sessionFactory;
	}

	private static Configuration configuration() {
		Properties settings = new Properties();

		settings.put(Environment.DRIVER, "com.mysql.cj.jdbc.Driver");
		settings.put(Environment.URL, "jdbc:mysql://localhost:3306/roytuts");
		settings.put(Environment.USER, "root");
		settings.put(Environment.PASS, "root");
		settings.put(Environment.DIALECT, "org.hibernate.dialect.MySQLDialect");
		settings.put(Environment.SHOW_SQL, "true");
		settings.put(Environment.FORMAT_SQL, "true");
		settings.put(Environment.CURRENT_SESSION_CONTEXT_CLASS, "thread");
		settings.put(Environment.HBM2DDL_AUTO, "update"); // or create
		// Deprecated
		// settings.put(Environment.QUERY_TRANSLATOR,
		// "org.hibernate.hql.internal.classic.ClassicQueryTranslatorFactory");

		Configuration configuration = new Configuration();

		configuration.setProperties(settings);
		configuration.addAnnotatedClass(Person.class);
		configuration.addAnnotatedClass(Student.class);
		configuration.addAnnotatedClass(Teacher.class);

		return configuration;
	}

}

Testing the Application

I am writing a class with main method to test the application for table per sub-class inheritance strategy.

public class HibernateInheritanceApp {

	public static void main(String[] args) {

		Student s = new Student();
		Teacher t = new Teacher();

		s.setId(1);
		s.setName("Student");
		s.setYear("1st Year");

		t.setId(2);
		t.setName("Teacher");
		t.setSubject("Physics");

		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		Transaction tx = null;
		try {
			tx = session.beginTransaction();

			session.save(s);
			session.save(t);

			tx.commit();
		} catch (HibernateException e) {
			e.printStackTrace();
		}

	}

}

Running the above main class you will see the following output in console:

Hibernate: 
    
    alter table student 
       add constraint FKf9p2k5e6ex24bg1vmchxk2upp 
       foreign key (skey) 
       references person (id)
Hibernate: 
    
    alter table teacher 
       add constraint FK735fpojxtiqnc9wfrluewotv9 
       foreign key (tkey) 
       references person (id)
Hibernate: 
    insert 
    into
        person
        (name, id) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        student
        (year, skey) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        person
        (name, id) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        teacher
        (subject, tkey) 
    values
        (?, ?)

You will see later that 1 copied into skey column of student table and 2 copied into tkey column of the teacher table.

Once the data have been successfully saved into the database the output will be:

Table

person

hibernate inheritance table per subclass

student

hibernate inheritance table per subclass

teacher

hibernate inheritance table per subclass

That’s all about table per subclass inheritance strategy in Hibernate framework.

Source Code

Download

Leave a Reply

Your email address will not be published. Required fields are marked *