Table per concrete class with XML
In previous article we saw how to implement Inheritance in Hibernate using Table per Hierarchy
In this article, we will learn the same using Table per Concrete class.
Let us understand about Table Per Concrete class with XML
In this strategy, each subclass table will have the subclass specific attributes and the attributes inherited from the parent class.
So each subclass will duplicate the parent class common attributes.
Let’s consider the below example
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 concrete class.
Employee table will have 3 attributes id,name and city from Employee class
PermanentEmployee table will have 4 attributes(3 from Employee class and 1 from itself)
ContractEmployee table will have 4 attributes(3 from Employee class and 1 from itself)
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
Step 2
Update pom.xml with Hibernate and Mysql dependencies
- <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>TablePerConcreteClassXML</groupId>
- <artifactId>TablePerConcreteClassXML</artifactId>
- <version>0.0.1-SNAPSHOT</version>
- <packaging>jar</packaging>
- <name>TablePerConcreteClassXML</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>TablePerConcreteClassXML</groupId> <artifactId>TablePerConcreteClassXML</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>TablePerConcreteClassXML</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
- 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
- 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
- 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
- <?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="Employee2">
- <id name="id">
- <generator class="increment"></generator>
- </id>
- <property name="name" column="Name"></property>
- <property name="city" column="City"></property>
- <union-subclass name="PermanentEmployee" table="PermanentEmployee">
- <property name="salary" column="Salary"></property>
- </union-subclass>
- <union-subclass name="ContractEmployee" table="ContractEmployee">
- <property name="hourlyRate" column="HourlyRate"></property>
- </union-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="Employee2"> <id name="id"> <generator class="increment"></generator> </id> <property name="name" column="Name"></property> <property name="city" column="City"></property> <union-subclass name="PermanentEmployee" table="PermanentEmployee"> <property name="salary" column="Salary"></property> </union-subclass> <union-subclass name="ContractEmployee" table="ContractEmployee"> <property name="hourlyRate" column="HourlyRate"></property> </union-subclass> </class> </hibernate-mapping>
We have defined only one mapping file for all 3 classes.
“union-subclass” tag is used for each subclass definition to specify the Table per concrete class inheritance.
“union-subclass” tag indicates that all the parent class columns will be union to Subclass tables
Each table defines columns for all properties of the class, including inherited properties.
If Parent class is abstract,then we have to map it with abstract=”true”.
Step 7
Create hibernate.cfg.xml
- <?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.
We have also provided the mapping xml file location using “mapping” tag.
Step 8
Create Hibernate util class
- 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
- 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 ContractEmployee ( id integer not null, Name varchar(255), City varchar(255), HourlyRate double precision, primary key (id) ) create table Employee2 ( id integer not null, Name varchar(255), City varchar(255), primary key (id) ) create table PermanentEmployee ( id integer not null, Name varchar(255), City varchar(255), Salary double precision, primary key (id) ) select max(ids_.mx) from ( select max(id) as mx from PermanentEmployee union select max(id) as mx from ContractEmployee union select max(id) as mx from Employee2 ) ids_ insert into Employee2 (Name, City, id) values (?, ?, ?) insert into PermanentEmployee (Name, City, Salary, id) values (?, ?, ?, ?) insert into ContractEmployee (Name, City, HourlyRate, id) values (?, ?, ?, ?) successfully persisted all the Employee records
We can see that 3 Create statements are executed to create 3 tables.
3 insert statements one for each object we persisted.
Check Table in MYSQL console
E:\MySql_Install\bin
Mysql –u root –p
Enter password
SELECT * FROM Employee2;
SELECT * FROM PermanentEmployee;
SELECT * FROM ContractEmployee;
We can see that id,Name and City columns of parent class table are duplicated in each subclass tables.
Advantage
Easy to implement and avoid the NULL values getting stored in the columns
Disadvantage
Columns in parent class table are duplicated in each subclass table.
Any change in the parent class table will impact all the subclass tables.
Note :This strategy is not completely normalized as it still have duplicate columns in each subclass table.