In our last article, we performed mapped inheritance between classes to database using Hibernate.
By default, Hibernate uses a single table strategy to perform
inheritance between the classes. And, there is another way through which Hibernate performs implement inheritance using single table inheritance i.e.
by using specific annotations with Hibernate.
Today we are going to understand how to map inheritance between the classes to the database using annotations with Hibernate. By using annotations,
we can configure the table name, column name and the values in dtype column of the single table created by Hibernate to implement inheritance.
Let's take the same example of an Entity class - Country, which is extended/inherited by two classes AsianCountry and EurpoeanCountry.
Now, let's see how Hibernate performs object-relational-mapping(ORM) to represent the inheritance between these three classes in the database, by using annotations.
Note :
In the upcoming example, We are going to create a Java class which will be mapped to a table in our database(Oracle Express Edition 10g).
We are going to use some Hibernate Annotations, hence, there will be no need to create the mapping-resource with extension hbm.xml .
For those who are not aware of Hibernate Annotations, please read
Hibernate Annotations for reference.
And yes, one more thing, we are going to create this program in an Integrated Development Environment (IDE),
so you may use any IDE of your choice.
Creating a primary/parent Entity class
We are going to create a general parent class that will contain all the Java code files
representing our Model class(whose objects needs to be persisted).
This parent class will be inherited by a few child classes that we are going to create later on.
This is a simple Java class whose objects needs to be persisted/saved, these objects are also known as Plain Old Java Objects(POJO) or Entity class.
Some may even refer to such class whose objects needs to be persisted as the Model class.
This class is an entity class, hence it will be preceded with the @Entity annotation plus a few other annotations described below.
@Inheritance - This annotation is placed just above the parent Entity class,
which tells the Hibernate that this class is the parent of inheritance relationship with other classes.
The strategy element of @Inheritance
annotations tells the Hibernate the strategy used to perform inheritance and for now we are going to use the default strategy - InhertanceType.SINGLE_TABLE.
@DiscriminatorColumn - This annotation allows you to change the name and type of column named dtype which
was created by default when Hibernate performed inheritance
without annotation.
Next, we are going to create the child Entity class inheriting from the parent Country class. The object of this child class will also be persisted in the database table.
The object of this class will contain a few specific properties such as continent and population to save these specifics about each country.
This class is also an entity class, hence it will be preceded with the @Entity annotation plus another annotation described below.
@DiscriminatorValue - This annotation allows you to change the name of class displayed in the dtype column
which was created by default when Hibernate performed inheritance without annotation.
AsianCountry.java
package decodejava;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
@Entity
@DiscriminatorValue ("Asian_Country")
public class AsianCountry extends Country {
private String continent;
private int population;
public String getContinent() {
return continent;
}
public void setContinent(String continent) {
this.continent = continent;
}
public int getPopulation()
{
return population;
}
public void setPopulation(int population)
{
this.population = population;
}
}
Creating the second child Entity class
Next, we are going to create another child Entity class inheriting from the parent Country class.
The object of this child class will also be persisted in the database table.
The object of this class will also contain a few specific properties to capture information about each country, such as its continent and population.
This class is also an entity class, hence it will be preceded with the @Entity annotation plus another annotation described below.
@DiscriminatorValue - This annotation allows you to change the name of class displayed in the dtype column
which was created by default when Hibernate performed inheritance without annotation.
EuropeanCountry.java
package decodejava;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
@Entity
@DiscriminatorValue ("European_Country")
public class EuropeanCountry extends Country
{
private String continent;
private int population;
public String getContinent()
{
return continent;
}
public void setContinent(String continent)
{
this.continent = continent;
}
public int getPopulation()
{
return population;
}
public void setPopulation(int population)
{
this.population = population;
}
}
Creating the class that calls the Hibernate API
This class will create some User_Details objects , which will be persisted using the Hibernate API and an
object-relational mapping(ORM) in the database is performed.
The Hiber class creates a Configuration object, used to configure the Hibernate. This is the first object we use when using the Hibernate.
This object is used to specify the location of a configuration file and
mapping document used by Hibernate. Using Configuration object we can create a SessionFactory object, which is eventually used to create a Session
object to perform the object persistence operations.
Note : We will not need to set the Id value of each object of parent class Country, as it is automatically done by using Hibernate's @GeneratedValue annotation.
Hiber.java
package decodejava;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class Hiber
{
public static void main(String[] args)
{
Configuration config = new Configuration();
SessionFactory sf = config.configure().buildSessionFactory();
Session session = sf.openSession();
Country country1 = new Country();
country1.setName("Asian Country");
AsianCountry asianCountry1 = new AsianCountry();
asianCountry1.setName("Japan");
asianCountry1.setContinent("Asia");
asianCountry1.setPopulation(127000000);
EuropeanCountry europeanCountry1 = new EuropeanCountry();
europeanCountry1.setName("Germany");
europeanCountry1.setContinent("Europe");
europeanCountry1.setPopulation(87000000);
session.beginTransaction();
session.save(country1); //Saving the first Country object object
session.save(asianCountry1); //Saving the first AsianCountry object
session.save(europeanCountry1); //Saving the first EuropeanCountry object
session.getTransaction().commit();
session.close();
}
}
Adding JARs
We are going to add some JARs files to the build path of our Java project.
These JARs make the Hibernate work with our database using a specific JDBC Driver for our particular
database.
All these JARs are included in the folder named required(within our Hibernate installation folder).
So, we need to add all the JARs in the required to our build path of our Java project.
Finally, we are going to add one more JAR file.
This is a specific JDBC JAR file(ojdbc14.jar) required by Hibernate to connect to our database(Oracle Express Edition 10g) and perform
object-relational mapping and object persistence.
Next, we are going to add a configuration file to our project.
This configuration document ends with an extension .cfg.xml,
and it is named as hibernate.cfg.xml.
This configuration file is an xml file and it allows us to specify the following features -
A database to which Hibernate connects to perform object-relational mapping and object persistence.
The JDBC driver class for our specific database, used by the Hibernate to connect to the database.
The username and password of database, using which the Hibernate connects to the database.
An SQL Dialect, used by the Hibernate to instruct the database.
A Entity/POJO class which will be mapped to a database table by Hibernate.
A command to echo all the SQL commands which are executed by the Hibernate on stdout.
The mapping entity class, used by Hibernate to correctly map the class to a database table. We will specify all three Entity classes
Country, AsianCountry and EuropeanCountry involved in inheritance.
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 settings -->
<property name="connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
<property name="connection.url">jdbc:oracle:thin:@localhost:1521:XE</property>
<property name="connection.username">system</property>
<property name="connection.password">promila21</property>
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">1</property>
<!-- SQL dialect -->
<property name="dialect">org.hibernate.dialect.Oracle10gDialect</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>
<!-- Drop and re-create the database schema on startup -->
<property name="hbm2ddl.auto">create</property>
<!-- Names the mapping entity class -->
<mapping class ="decodejava.Country"/>
<mapping class ="decodejava.AsianCountry"/>
<mapping class ="decodejava.EuropeanCountry"/>
</session-factory>
</hibernate-configuration>
Directory Structure of Hibernate Project
The picture above depicts how and where to arrange POJO/Entity(.java) file, its two child Entity classes and the class(that calls the hibernate into action)
in a specific directory structure.
Project Folder - HibernateSingleTableInheritanceAnnotation is the name of our Project and it is a top-level directory.
This project folder contains the main package of our project i.e. decodejava,
which contains POJO/Entity class file i.e. Country.java, its first child class i.e. AsianCountry.java, second child class i.e.
EuropeanCountry.java and the class that calls the hibernate into action i.e. Hiber.java.
Besides this, the project folder also contains the hibernate configuration file i.e. hibernate.cfg.xml, and
as we have used some Hibernate Annotations in this project, hence, there will be no need to create the mapping-resource
with extension hbm.xml .
The project folder also contains all the needed Hibernate JARs present in the required folder of Hibernate
installation and the JDBC JAR file(ojdbc14.jar) required by Hibernate to connect to our database.
Execution
When you execute the Hiber.java class, you will see the following output -
Hibernate: create sequence hibernate_sequence start with 1 increment by 1
Hibernate: create table SingleTableInheritance_Country (Class_Type varchar2(31 char) not null, id number(10,0) not null, name varchar2(255 char), continent varchar2(255 char), population number(10,0), primary key (id))
Hibernate: select hibernate_sequence.nextval from dual
Hibernate: select hibernate_sequence.nextval from dual
Hibernate: select hibernate_sequence.nextval from dual
Hibernate: insert into SingleTableInheritance_Country (name, Class_Type, id) values (?, 'Country', ?)
Hibernate: insert into SingleTableInheritance_Country (name, continent, population, Class_Type, id) values (?, ?, ?, 'Asian_Country', ?)
Hibernate: insert into SingleTableInheritance_Country (name, continent, population, Class_Type, id) values (?, ?, ?, 'European_Country', ?)
This output shows you all the SQL commands executed by the Hibernate within the database to map the inheritance between the Java classes
to a database table and perform all the
activities of creating the tables and saving the objects of all three Entity classes.
Finally, after executing Hiber class a new table will be constructed -
SingleTableInheritance_Country, representing the inheritance between the three classes in the database.
Moreover, if you type the following SQL query in the database -
select * from SingleTableInheritance_Country;
you will get the following output displaying -
The objects of the parent Country Entity class.
The objects of child AsianCountry Entity class.
The objects of another child EuropeanCountry Entity class.
This table shows you how hibernate has presented the inheritance between the three classes using annotations
and have allowed us to configure the table name, column name and the values in dtype column(which was created when we didn't use annotations) using the default single table strategy.
In the upcoming articles, we are going to look at more strategies used by Hibernate to implement inheritance between classes.