Table per hierarchy with Annotation


Let us understand about Table Per Hierarchy with Annotation


We have already discussed Table per hierarchy with xml.

Please go through Table per hierarchy with xml article before going through the same using annotation.

Let’s create hibernate project using Annotation

Step 1

Create hibernate project

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

Project Structure

TPH_Annotation_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
41
42
43
44
45
46
47
48
<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>TablePerHierarchyAnnotation</groupId>
    <artifactId>TablePerHierarchyAnnotation</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
 
    <name>TablePerHierarchyAnnotation</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>
 
        <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>
 
        <!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-annotations -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-annotations</artifactId>
            <version>3.5.6-Final</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>TablePerHierarchyAnnotation</groupId>
	<artifactId>TablePerHierarchyAnnotation</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>TablePerHierarchyAnnotation</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>

		<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>

		<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-annotations -->
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-annotations</artifactId>
			<version>3.5.6-Final</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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package com.kb.model;
 
import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;
 
@Entity
@Table(name = "Employee")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.STRING)
@DiscriminatorValue(value = "E")
 
public class Employee {
 
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private int id;
 
    @Column(name = "name")
    private String name;
 
    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;
    }
}
package com.kb.model;

import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;

@Entity
@Table(name = "Employee")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.STRING)
@DiscriminatorValue(value = "E")

public class Employee {

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column(name = "id")
	private int id;

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

	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;
	}
}

Step 4

Create PermanentEmployee class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.kb.model;
 
import javax.persistence.Column;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
 
@Entity
@DiscriminatorValue(value = "PE")
public class PermanentEmployee extends Employee {
 
    @Column(name = "salary")
    private double salary;
 
    public double getSalary() {
        return salary;
    }
 
    public void setSalary(double salary) {
        this.salary = salary;
    }
 
}
package com.kb.model;

import javax.persistence.Column;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;

@Entity
@DiscriminatorValue(value = "PE")
public class PermanentEmployee extends Employee {

	@Column(name = "salary")
	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
15
16
17
18
19
20
21
22
package com.kb.model;
 
import javax.persistence.Column;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
 
@Entity
@DiscriminatorValue(value = "CE")
public class ContractEmployee extends Employee {
    
    @Column(name = "hourlyRate")
    private double hourlyRate;
 
    public double getHourlyRate() {
        return hourlyRate;
    }
 
    public void setHourlyRate(double hourlyRate) {
        this.hourlyRate = hourlyRate;
    }
 
}
package com.kb.model;

import javax.persistence.Column;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;

@Entity
@DiscriminatorValue(value = "CE")
public class ContractEmployee extends Employee {
	
	@Column(name = "hourlyRate")
	private double hourlyRate;

	public double getHourlyRate() {
		return hourlyRate;
	}

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

}

Step 6

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
39
40
41
<?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">100</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 class="com.kb.model.Employee" />
        <mapping class="com.kb.model.ContractEmployee" />
        <mapping class="com.kb.model.PermanentEmployee" />
 
    </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">100</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 class="com.kb.model.Employee" />
		<mapping class="com.kb.model.ContractEmployee" />
		<mapping class="com.kb.model.PermanentEmployee" />

	</session-factory>

</hibernate-configuration>


We have defined the entire database configuration in hibernate.cfg.xml 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.

We have also provided the mapping class names where we have added the annotations using “mapping” tag.

Step 7

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 8

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");  
	}

}


We have created 3 records, one for each Employee, PermanentEmployee and ContractEmployee class.

When we persist these 3 records of different objects, It will be saved in a Single table.

Step 9

Run the above class to check the output

Hibernate: 
    
    create table Employee1 (

       id integer not null,

        discriminator varchar(255) not null,

        name varchar(255),

        salary double precision,

        hourlyRate double precision,

        primary key (id)
    )

select
        next_val as id_val 

    from
        hibernate_sequence for update
      

      
Hibernate: 

    update
        hibernate_sequence 

    set
        next_val= ? 

    where
        next_val=?


Hibernate: 

    select
        next_val as id_val 

    from
        hibernate_sequence for update
  
          
Hibernate: 

    update
        hibernate_sequence 

    set
        next_val= ? 

    where
        next_val=?


Hibernate: 

    select
        next_val as id_val 

    from
        hibernate_sequence for update
         
  
Hibernate: 

    update
        hibernate_sequence 

    set
        next_val= ? 

    where
        next_val=?


Hibernate: 

    insert 
    into
        Employee
        (NAME, TYPE, ID) 
    values
        (?, 'E', ?)


Hibernate: 
    insert 
    into
        Employee
        (NAME, SALARY, TYPE, ID) 
    values
        (?, ?, 'PE', ?)


Hibernate: 
    insert 
    into
        Employee
        (NAME, HOURLYRATE, TYPE, ID) 
    values
        (?, ?, 'CE', ?)


successfully persisted all the Employee records


We can see that Create statement is executed only once as it creates a Single table for all the classes.

We can see 3 statements for updating the auto generated primary key for ID is executed.

3 insert statements one for each object we persisted is executed.

Check the Table in MYSQL workbench

SELECT * FROM javainsimpleway.employee1;

TPH_Output1

Check Table in MYSQL console

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

SELECT * FROM javainsimpleway.employee1;

TPH_Output2

We can see NULL values are stored in Salary column for Contract employee and NULL values is stored in HourlyRate column for Permanent employees.

We can see NULL values stored in salary and hourlyRate columns for Employee record.

We can see that discriminator column can be used to identify the type of the record.

Download this project TablePerHierarchyAnnotation.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