Table Per Subclass with XML

In Table Per Concrete class with xml we have seen how to implement Inheritance in Hibernate using Table per concrete class

In this article, we will learn the same using most efficient strategy called “Table per Subclass”.

Let us understand about Table Per Subclass with XML


In this strategy, each subclass table will have the subclass specific attributes only.

Parent class attributes will be stored in Parent class table.

Subclass tables will have foreign key associations to the superclass table

So there will not be any duplicate columns in the subclass table except one column which is anyway required to maintain the relation between Parent and subclass tables through foreign key.

In this strategy, number of tables is equal to number of classes.

Let’s consider the below example


TPCC

In this example, we have 3 classes where Employee class is the super class for both PermanentEmployee and ContractEmployee classes.

We will have 3 tables created one for each class.

Let’s create hibernate project using this strategy

Step 1

Create hibernate project

Please refer Hibernate setup in eclipse article on how to do it.

Project structure


TPSC_XML_Proj_structure

Step 2

Update pom.xml with Hibernate and Mysql dependencies

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<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>TablePerSubclassXML</groupId>
  <artifactId>TablePerSubclassXML</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>
 
  <name>TablePerSubclassXML</name>
  <url>http://maven.apache.org</url>
 
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
 
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
 
     <!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-core -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>5.2.6.Final</version>
        </dependency>
 
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>6.0.5</version>
        </dependency>
 
  </dependencies>
</project>
<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>TablePerSubclassXML</groupId>
  <artifactId>TablePerSubclassXML</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>TablePerSubclassXML</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>

     <!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-core -->
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-core</artifactId>
			<version>5.2.6.Final</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>6.0.5</version>
		</dependency>

  </dependencies>
</project>

Step 3

Create Employee class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.kb.model;
 
public class Employee {
    private int id;
    private String name;
    private String city;
    
    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;
    }
    public String getCity() {
        return city;
    }
    public void setCity(String city) {
        this.city = city;
    }
}
package com.kb.model;

public class Employee {
	private int id;
	private String name;
	private String city;
	
	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;
	}
	public String getCity() {
		return city;
	}
	public void setCity(String city) {
		this.city = city;
	}
}

Step 4

Create PermanentEmployee class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.kb.model;
 
public class PermanentEmployee extends Employee{
    private double salary;
 
    public double getSalary() {
        return salary;
    }
 
    public void setSalary(double salary) {
        this.salary = salary;
    }
 
}
package com.kb.model;

public class PermanentEmployee extends Employee{
	private double salary;

	public double getSalary() {
		return salary;
	}

	public void setSalary(double salary) {
		this.salary = salary;
	}

}

Step 5

Create ContractEmployee class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.kb.model;
 
public class ContractEmployee extends Employee{
    private double hourlyRate;
 
    public double getHourlyRate() {
        return hourlyRate;
    }
 
    public void setHourlyRate(double hourlyRate) {
        this.hourlyRate = hourlyRate;
    }
 
}
package com.kb.model;

public class ContractEmployee extends Employee{
	private double hourlyRate;

	public double getHourlyRate() {
		return hourlyRate;
	}

	public void setHourlyRate(double hourlyRate) {
		this.hourlyRate = hourlyRate;
	}

}

Step 6

Create Employee.hbm.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?xml version='1.0' encoding='UTF-8'?>  
<!DOCTYPE hibernate-mapping PUBLIC  
          "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  
          "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">  
  
<hibernate-mapping package="com.kb.model">  
       <class name="Employee" table="Employee">  
              <id name="id" column="Id">  
                   <generator class="native"></generator>  
              </id>  
              <property name="name" column="Name"></property>  
              <property name="city" column="City"></property> 
            
         <joined-subclass name="PermanentEmployee" table="PermanentEmployee">
              <key column="Id"/>
              <property name="salary" column="Salary"></property>  
         </joined-subclass>
            
         <joined-subclass name="ContractEmployee" table="ContractEmployee">
              <key column="Id"/>
              <property name="hourlyRate" column="HourlyRate"></property>  
         </joined-subclass>  
 
      </class>  
 
</hibernate-mapping>  
<?xml version='1.0' encoding='UTF-8'?>  
<!DOCTYPE hibernate-mapping PUBLIC  
          "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  
          "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">  
  
<hibernate-mapping package="com.kb.model">  
       <class name="Employee" table="Employee">  
              <id name="id" column="Id">  
                   <generator class="native"></generator>  
              </id>  
              <property name="name" column="Name"></property>  
              <property name="city" column="City"></property> 
            
         <joined-subclass name="PermanentEmployee" table="PermanentEmployee">
              <key column="Id"/>
              <property name="salary" column="Salary"></property>  
         </joined-subclass>
            
         <joined-subclass name="ContractEmployee" table="ContractEmployee">
              <key column="Id"/>
              <property name="hourlyRate" column="HourlyRate"></property>  
         </joined-subclass>  

      </class>  

</hibernate-mapping>  


We have defined only one mapping file for all 3 classes.

joined-subclass” tag is used for each subclass definition to specify the Table per Subclass inheritance.

“joined-subclass” tag indicates that parent class and Subclass tables maintain a relation using Join based on Primary key foreign key relationship.

key column in each subclass should be same as Primary key column name in Parent class.

Each Subclass table will have columns only specific to Subclass attributes.

Step 7

Create hibernate.cfg.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
       "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
       "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
 
<hibernate-configuration>
 
   <session-factory>
 
       <!-- Database connection properties -->
       <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
       <property name="connection.url">jdbc:mysql://localhost/javainsimpleway</property>
       <property name="connection.username">root</property>
       <property name="connection.password">root</property>
 
       <!-- JDBC connection pool (using the built-in) -->
       <property name="connection.pool_size">1</property>
 
       <!-- SQL dialect -->
       <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
 
       <!-- Disable the second-level cache -->
       <property name="cache.provider_class">org.hibernate.cache.internal.NoCacheProvider</property>
 
       <!-- Echo all executed SQL to stdout -->
       <property name="show_sql">true</property>
       
       <!-- Format the generated Sql -->
       <property name="format_sql">true</property>
 
       <!-- Dont Drop and re-create the database schema on startup,Just update it -->
       <property name="hbm2ddl.auto">update</property>
 
       <mapping resource="com/kb/mapping/employee.hbm.xml"/>
 
   </session-factory>
 
</hibernate-configuration>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
       "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
       "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
 
<hibernate-configuration>
 
   <session-factory>
 
       <!-- Database connection properties -->
       <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
       <property name="connection.url">jdbc:mysql://localhost/javainsimpleway</property>
       <property name="connection.username">root</property>
       <property name="connection.password">root</property>
 
       <!-- JDBC connection pool (using the built-in) -->
       <property name="connection.pool_size">1</property>
 
       <!-- SQL dialect -->
       <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
 
       <!-- Disable the second-level cache -->
       <property name="cache.provider_class">org.hibernate.cache.internal.NoCacheProvider</property>
 
       <!-- Echo all executed SQL to stdout -->
       <property name="show_sql">true</property>
       
       <!-- Format the generated Sql -->
       <property name="format_sql">true</property>
 
       <!-- Dont Drop and re-create the database schema on startup,Just update it -->
       <property name="hbm2ddl.auto">update</property>
 
       <mapping resource="com/kb/mapping/employee.hbm.xml"/>
 
   </session-factory>
 
</hibernate-configuration>


We have defined all the database configuration in this file

hbm2ddl.auto property is defined in the config file which helps in automatic creation of tables in the database based on the mapping.

If hbm2ddl.auto is “update” then it avoids the dropping and recreation of schema and tables

We have also provided the mapping xml file location using “mapping” tag.

Step 8

Create Hibernate util class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.kb.util;
 
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
 
public class HibernateUtil {
    private static final SessionFactory sessionFactory = buildSessionFactory();
 
    private static SessionFactory buildSessionFactory() {
        try {
            // Create the SessionFactory from hibernate.cfg.xml
            return new Configuration().configure().buildSessionFactory();
        } catch (Throwable ex) {
            // Make sure you log the exception to track it
            System.err.println("SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }
 
    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }
    
    public static void shutdown() {
        // Optional but can be used to Close caches and connection pools
        getSessionFactory().close();
    }
 
}
package com.kb.util;

import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtil {
	private static final SessionFactory sessionFactory = buildSessionFactory();

    private static SessionFactory buildSessionFactory() {
        try {
            // Create the SessionFactory from hibernate.cfg.xml
            return new Configuration().configure().buildSessionFactory();
        } catch (Throwable ex) {
            // Make sure you log the exception to track it
            System.err.println("SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }
    
    public static void shutdown() {
    	// Optional but can be used to Close caches and connection pools
    	getSessionFactory().close();
    }

}

Step 9

Create main class to interact with DB

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package com.kb.db;
 
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
 
import com.kb.model.ContractEmployee;
import com.kb.model.Employee;
import com.kb.model.PermanentEmployee;
import com.kb.util.HibernateUtil;
 
public class Main {
    public static void main(String[] args) {
        //Get session factory using Hibernate Util class
        SessionFactory sf = HibernateUtil.getSessionFactory();
        //Get session from Sesson factory
        Session session = sf.openSession();
        
        //Begin transaction
        Transaction t=session.beginTransaction();  
 
        //Creating Employee base class record
        Employee employee=new Employee();  
        employee.setName("John");  
        
        //Creating Permanent Employee subclass record
        PermanentEmployee permanentEmployee=new PermanentEmployee();  
        permanentEmployee.setName("Jacob");  
        permanentEmployee.setSalary(30000);  
        
       //Creating Contract Employee subclass record
        ContractEmployee contractEmployee=new ContractEmployee();  
        contractEmployee.setName("Raj");  
        contractEmployee.setHourlyRate(2000);  
        
        //persist all the employee records
        session.persist(employee);  
        session.persist(permanentEmployee);  
        session.persist(contractEmployee);  
        
        //Commit the transaction and close the session
        t.commit();  
        session.close();  
        System.out.println("successfully persisted all the Employee records");  
    }
 
}
package com.kb.db;

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

import com.kb.model.ContractEmployee;
import com.kb.model.Employee;
import com.kb.model.PermanentEmployee;
import com.kb.util.HibernateUtil;

public class Main {
	public static void main(String[] args) {
		//Get session factory using Hibernate Util class
		SessionFactory sf = HibernateUtil.getSessionFactory();
		//Get session from Sesson factory
		Session session = sf.openSession();
		
		//Begin transaction
		Transaction t=session.beginTransaction();  

		//Creating Employee base class record
		Employee employee=new Employee();  
		employee.setName("John");  
	    
		//Creating Permanent Employee subclass record
	    PermanentEmployee permanentEmployee=new PermanentEmployee();  
	    permanentEmployee.setName("Jacob");  
	    permanentEmployee.setSalary(30000);  
	    
	   //Creating Contract Employee subclass record
	    ContractEmployee contractEmployee=new ContractEmployee();  
	    contractEmployee.setName("Raj");  
	    contractEmployee.setHourlyRate(2000);  
	    
	    //persist all the employee records
	    session.persist(employee);  
	    session.persist(permanentEmployee);  
	    session.persist(contractEmployee);  
	    
	    //Commit the transaction and close the session
	    t.commit();  
	    session.close();  
	    System.out.println("successfully persisted all the Employee records");  
	}

}

Step 10

Run the above class to check the output

Hibernate: 
    
    create table Employee (

       Id integer not null auto_increment,

        Name varchar(255),

        City varchar(255),

        primary key (Id)
    )


create table ContractEmployee (

       Id integer not null,

        HourlyRate double precision,

        primary key (Id)
    )

    
    create table PermanentEmployee (

       Id integer not null,

        Salary double precision,

        primary key (Id)
    )
    

    alter table ContractEmployee 

       add constraint FKjtjm93uys6lvom6tbfgl7l9e8 

       foreign key (Id) 

       references Employee (Id)


Hibernate: 
    
    alter table PermanentEmployee 

       add constraint FKg52k74r5b4s4asd3t152d7jg2 

       foreign key (Id) 

       references Employee (Id)


Hibernate: 

    insert 

    into

        Employee
        (Name, City) 

    values
        (?, ?)


Hibernate: 

    insert 

    into

        Employee
        (Name, City) 

    values
        (?, ?)


Hibernate: 

    insert 

    into

        PermanentEmployee
        (Salary, Id) 

    values
        (?, ?)


Hibernate: 

    insert 

    into

        Employee
        (Name, City) 

    values
        (?, ?)


Hibernate: 

    insert 

    into

        ContractEmployee
        (HourlyRate, Id) 

    values
        (?, ?)

successfully persisted all the Employee records


We can see that 3 Create statements are executed to create 3 tables.

We can see 2 alter statements are executed to create a relation between parent class table and subclass tables.

3 insert statements one for each object we persisted and other 2 insert statements are executed to insert parent table for every insert we do for subclass to maintain the relationship.

Check Table in MYSQL console

E:\MySql_Install\bin
Mysql –u root –p
Enter password

SELECT * FROM Employee;

SELECT * FROM PermanentEmployee;

SELECT * FROM ContractEmployee;

TPSC_Output1

We can see that id,name and city columns are stored only in parent class table.

Each subclass table having only subclass specific columns with one extra column “id” to maintain the Join relationship with the parent class tables.

Advantage

It provides more normalized database tables.

When we modify Parent class table, Child table will not get affected as it already maintains a relation with Parent.

No duplicate columns in any table except “id” column which is required to maintain the relation.

Disadvantage

If the inheritance hierarchy keeps growing, it results in poor performance as it has to maintain the relationship between parent and child class tables.

It has to perform join to access child class attributes as some of the Child class attributes are stored in Parent class table using a relationship.

Note :This hierarchy is almost completely normalized and can be best used if hierarchy of inheritance is not too depth.


Download this project TablePerSubclassXML.zip

About the Author

Karibasappa G C (KB)
Founder of javainsimpleway.com
I love Java and open source technologies and very much passionate about software development.
I like to share my knowledge with others especially on technology 🙂
I have given all the examples as simple as possible to understand for the beginners.
All the code posted on my blog is developed,compiled and tested in my development environment.
If you find any mistakes or bugs, Please drop an email to kb.knowledge.sharing@gmail.com

Connect with me on Facebook for more updates

Share this article on