object relationship mapping and hibernate
TRANSCRIPT
Object Relationship Mapping (ORM)
ORM stands for Object relational Mapping. ORM is an attempt to map the notion of object and
relational world so that they can talk to each other in a easy way. Any non trivial application has a
database behind it and Java applications are no exception. In fact if we look closely into any
application, one will realize that the application gets more or less modeled around the data model. In
database technology, relational database are the clear winners. Other database technologies has
come and gone. Relational concept of data management was first introduced by E.F. Codd in 1970. An
analogy for relational model can be drawn with spreadsheets. Each sheet represents a table and the
columns in the sheet represent the table attributes. Each instance of data is represented by the rows.
The data in different sheets are connected with each other by referring to the data point using the
sheet number, column number and row number. This is what is called as foreign key relationship in
database technology. In fact most of the GUI interfaces to database show the data in a spreadsheet
format.
To interact with the database, Standard Query Language(SQL) has emerged as the standard way. The
SQL standards are controlled by ANSI. However there are still proprietary variations to it. SQL provides
two types of mechanism:
Data Definition Language (DDL) : Provides ways to create and alter tables.
Data Manipulation Language (DML) : Provides ways to manipulate and retrieve data. It includes inserting,
updating and deleting data.
To interact with the database, the applications has to issue SQL to the database. How to issue SQL is
proprietary to each database. They have their own API's exposed for this and the API's might be
written in different languages. For example a database written in C language. might expose C based
API's. Considering that the data independence is considered a virtue for any application, it would be a
lot of work for an application developer to understand the interfaces for each of the database and
implement it. To solve this kind of porblem, Java has come up with ((JDBC)) API's.
JDBC is the most popular way of connecting to databases in Java. It's an interface based API where the
implementation for each database is provided by the drivers for particular database. Though JDBC is
very popular, it is inherently relational in nature. The basic problem is the mismatch in conceptual
level between relational technology and Object Oriented Technology. Java being a pure Object
Oriented Language, this mismatch is important to deal with. This mismatch is also known as Object
relational mismatch. ORM tries to solve this mismatch.
Let's see the kind of mismatch that are there:
Inheritance
Java supports inheritance. For example we might have User class from which Student and Teacher
class is derived.
User
public class User{
private String Name;
//Setters and getters
}
Student
public class Student extends User{
private double percentage;
//Setter and Getter
}
Teacher
public class Teacher extends User{
private int exprienceYears;
//Setters and Getters
}
Now think for a moment how you are going to map these classes to the table structure. ORM
frameworks adopt different strategies to solve this, which can be seen at ((Hibernate)) section.
Also with this comes the mismatch in terms of polymorphism. A reference of User type can refer to an
object of Student or Teacher. Also a list might contain a mix of Teacher, Student and User objects. How
you build those list by querying database. The ORM frameworks has to somehow differentiate that the
data is belonging to User or Student or Teacher.
Granularity
The granularity problem comes when the number of classes mapping to number of tables in the
database do not match. For example let's say we have the User class which has an Address object
public class User{
private String Name;
private Address address;
//Setters and getters
}
Address
public class Address{
private String city;
private String country;
//Setters and getters
Also the table structure for User is
Table USER:
NAME
CITY
COUNTRY
There is one table but the data is sitting in two objects. The same problem can come the other way
round also where you have two tables and one class containing all the data points. ORM frameworks
has to care of this mismatch in terms of different number of tables mapped to different number of
classes.
Identity and Equality
The notion of identity is driven by primary key in relational model. Given a primary key you will always
retrieve the same data. Also it does not matter, how many clients are retrieving the same data. With
right isolation level all will see the same data. In Java the situation becomes more complex. The same
data row might be represented by more objects in Java layer. For example a User data in database
with primary key 1 might have more than one object in different thread. The question comes which is
the object having the right data. Also if all thread try to save the same object, than who wins? Similar
problem arises related to equality. In Java the default is reference equality. If the references are
pointing to the same object than they are equal. The corollary is that if there are two objects
representing the same data row, they will come out as different. To solve this we have to give
implementation to the equals methos, but it's not always trivial. ORM solutions has to provide
provisions to maintain the correct notion of equality and identity. The frameworks usually ask to
explicitly map the primary key to an attribute.
Association
In Java the relationship is done by association. For example User has a reference of address. However
in Tables, the relationship is done with foreign key association. Java has also the notion of
directionality. For example you can access address form User but not the other way round. To build the
relationship from both side you have to put the reference on both the sides.
User
public class User{
private Address address;
//Setters and Getters
}
Address
public class Address{
private User user;
//setters and getters
}
However there is no notion of directionality in the relational world.The foreign key relationship can be
done at any one end and the relationship is build. With SQL you can navigate from any end. For
example the foreign key relationship build at User side will be
Table ADDRESS
ADDRESS_ID
CITY
COUNTRY
Table USER
USER_ID
NAME
ADDRESS_ID (Fk)
ORM solutions has to deal with these association while setting and getting the data from the database.
Type Systems
Object Oriented and relational world has different type systems. For example in relational world string
can be constrained on the size however on the Java side you can point the reference of String to any
size based on memory allocated. Also date and times are handled differently in Java and relational
world. For example in some databases there is a distinction between date, time and timestamp. Time
stamp contains both date and time. In Java, the date is a long value which contains the date and time
both. So when you fetch the date or time from relational world how you map it to a Java data type of
Date or Calendar.
Databases are Different
There are so many popular databases in the market. In spite of SQL being a standard, there are still
variations in the SQL support in each database and there are many vendor extensions and individual
flavors in each database. Though this is not really an ORM issue but it is important from the
perspective of database portability, if you are looking for it. Ensuing database portability using JDBC is
usually a hurricane task if you wish to use the individual flavors of database for performance or other
reasons. ORM frameworks attempt to handle most of the important databases transparently. Also they
do have extension mechanisms if you wish to support another database
A video explaining the above concepts:
ORM frameworks adopt different strategies to solve these kind of mismatches. ORM frameworks strive
to preserve the notion of object world concepts and shield the developers from relational world by
taking care of mappings. This should be taken as an excuse to not to learn relational concepts. In fact
on the other way to be a good user of ORM frameworks, one should understand how the mapping
works and the implications of it. The number issue in using ORM frameworks is performance and most
of the time its because of not understanding how the ORM frameworks map to the relational world and
not having a good grasp of relational world concepts.
Hibernate Introduction
Hibernate is an ORM framework. It provides capability to interact with database in an object oriented
way. Hibernate provides its own HQL (Hibernate query language) also. The concepts of ORM are now
adopted in EJB3.0 in the form of entity beans and Jave Persistence API (JPA). Hibernate supports Entity
beans also. This means hibernate can act as an implementation provider for Entity beans. In fact, JBoss
uses hibernate as implementation provider for Entity beans. Hibernate has its own set of API's also
which at the moment are richer than JPA. Going forward however we might see Entity beans and JPA
closing this gap.
Let's make a Java project where we will persist the data into the database. First download the
Hibernate Core from https://www.hibernate.org/6.html. Unzip the download at a location and you will
find the following important directories/file in it (This might be different from what you see as
hibernate keeps on changing the structure).
doc - contains the reference document for hibernate
eg - example application
etc - configuration files
grammer
lib - contains dependencies for hibernate. See lib/_README.txt for details about libraries.
src - source code of hibernate
test - test suite
hibernate3.jar - hibernate library
Make a simple Java application in your IDE. Have the following libraries in the build path:
From Hibernate Core (<hibernate-distribution-*>/lib/required)
antlr-2.7.6
commons-collections-3.1.jar
dom4j-1.6.1.jar
javassist-3.9.0.GA.jar
jta-1.1.jar
slf4j-api-1.5.8.jar
<hibernate-distribution-*>/lib/ -Choose between cglib or javassist
<hibernate-distribution>/*hibernate3.jar
Download slf4j from http://www.slf4j.org/dist/
slf4j-simple-1.5.8.jar
hsqldb.jar - We will be using hypersonic database for this example. you need to download hypersonic jar.
Hibernate is a Object relational mapping framework. What it will help us is in saving the objects into
the relational world. So let's have our Java class whose object instances at runtime will be saved by
hibernate to the database.
public class Student {
//Id will correspond the primary key in the database
private Long id;
protected String name;
public Long getId() { return id; }
public void setId(Long id) {this.id = id;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
}
Now we have the domain class definition with us. Now we will put a mapping file which will map this
class to the data model in the database. We are not creating the table in the database as we will use
hibernate to generate the data model. Though in real life application you would have your data model
already created and placed in the database.
Student.hbm.xml (Put it together with Student class)
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!-- class attributes -
name - Java class name
table - Table to which this class is mapped
schema - The schema where it sits
id -
name - name of property on Java class. It will result in getId() and setId method
column - column in table to which it is mapped
generator- how the id's to be generated
-->
<class name="com.oyejava.Student" table="Student" schema="PUBLIC">
<id name="id" column="STUDENT_ID">
<generator class="increment"/>
</id>
<!-- name - name of property in Java class. getName() and setName()
column - column in table to which it is mapped. -->
<property name="name" column="NAME" />
</class>
</hibernate-mapping>
Now we will provide hibernate the details to connect to the database. Similar to JDBC coding, the
connection details and drivers need to be provided to Hibernate. We will use hsql, the details of which
can be seen at HypersonicThis is done in a XML file.
hibernate.cfg.xml (Put it in the class path)
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- Database connection settings -->
<!-- If the below drive does not works try org.hsqldb.jdbc.JDBCDriver-->
<property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
<property name="connection.url">jdbc:hsqldb:hsql://localhost</property>
<property name="connection.username">sa</property>
<property name="connection.password"></property>
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">1</property>
<!-- SQL dialect - This tells the SQL grammer to be used -->
<property name="dialect">org.hibernate.dialect.HSQLDialect</property>
<!-- Enable Hibernate's automatic session context management -->
<property name="current_session_context_class">thread</property>
<!-- Disable the second-level cache -->
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
<!-- Log out all the sql that hibernate is issuing to datbase.
This is very useful for debugging -->
<property name="show_sql">true</property>
<!-- Create the table looking at class and mapping. Very useful in development
Use validate in production environments. -->
<property name="hbm2ddl.auto">create</property>
<!-- Mapping file. -->
<mapping resource="com/oyejava/Student.hbm.xml"/>
</session-factory>
</hibernate-configuration>
Now we will write a utility class which will start the session factory of hibernate, if it is not started and
will return the Session factory. This is the most used pattern of boot strapping Hibernate. Session
factory contains all the configuration information at runtime. A Session is retrieve from the Session
factory. A session is a light weight object which provide services to interact with database. You can
think of Session like JDBC connection, thought it may not open the database connection if not required.
HibernateUtil
public class HibernateUtil {
private static SessionFactory sessionFactory;
static{
try{
//By default it will look for hibernate.cfg.xml in the class path
sessionFactory=new Configuration().configure().buildSessionFactory();
}catch(Throwable ex){
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory(){
return sessionFactory;
}
public static void shutdown(){
//Close caches and connection pool
getSessionFactory().close();
}
}
Now let's write the main class in which we will create a Student object and persist it to database
public class HibernateBasic {
public static void main(String[] args) {
//Get the session from the Session factory
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx= session.beginTransaction();
Student student = new Student();
student.setName("oyeJava");
Long studentId = (Long)session.save(student);
System.out.println(studentId);
tx.commit();
session.close();
}
}
Run the application and check in the database. It should persist a row of data. Download the source
code from down below in the attachment section. The libraries has to be included as per the list given
above.
In the above example we saw how to configure hibernate using XML mapping. The present trend is
however to move towards annotation based mapping.
Hibernate Introduction with Annotation
Before Java 5, Hibernate has XML way of configuring things. However with introduction of Annotations,
the world has changed a lot. Many frameworks has adopted annotations as an alternative to XML.
Hibernate has also adopted annotation. The XML way is still supported but annotation is the
predominant way of working now. We will do the same example as we did in Hibernate Introduction
using annotation.
Download the Hibernate Core and Hibernate Annotations from https://www.hibernate.org/6.html. Let's
make a Java project and pull the following libraries.
From Hibernate Core (<hibernate-distribution-*>/lib/required)
antlr-2.7.6
common-collections-3.1.jar
dom4j-1.6.1.jar
hibernate3.jar
javassist-3.9.0.GA.jar
jta-1.1.jar
slf4j-api-1.5.8.jar
<hibernate-distribution-*>/lib/ -Choose between cglib or javassist
From Hibernate Annotation:
lib/ejb3-persistence.jar
lib/hibernate-commons-annotations.jar
hibernate-annotations.jar
Download slf4j from http://www.slf4j.org/
slf4j-simple-1.5.8.jar
Hypersonic:
hsqldb.jar - We will be using hypersonic database for this example. you need to download hypersonic jar.
For details see ((Hypersonic)).
Now let's write our domain class which is Student class.
Student
//Entity annotation represents that it is an entity class and is
//mapped to a table in database. Table annotation tells about the
//table name to which this entity is mapped
@Entity
@Table(name="Student")
public class Student {
//Id will correspond the primary key in the database
private Long id;
protected String name;
//Id - Represents that it is a primary key column
//GeneratedValue - How the key to be generated
//column - Column to which this property is mapped
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name="STUDENT_ID")
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
//There is annotation here so by default it is mapped to
//the column with name NAME. In annotation, the properties are
//by default mapped. In XML mapping by default the columns
//are not mapped.
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
If you have done the Hibernate Introduction, you will realize that the mapping has been pulled in the
class file itself in terms of annotations. The parallel can easily be drawn. Also now we do not need any
mapping hbm.xml file. We still need the configuration XML, though the mapping will be done by
referencing to class
hibernate.cfg.xml (Put it in the class path)
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- Database connection settings -->
<property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
<property name="connection.url">jdbc:hsqldb:hsql://localhost</property>
<property name="connection.username">sa</property>
<property name="connection.password"></property>
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">1</property>
<!-- SQL dialect - This tells the SQL grammer to be used -->
<property name="dialect">org.hibernate.dialect.HSQLDialect</property>
<!-- Enable Hibernate's automatic session context management -->
<property name="current_session_context_class">thread</property>
<!-- Disable the second-level cache -->
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
<!-- Log out all the sql that hibernate is issuing to datbase.
This is very useful for debugging -->
<property name="show_sql">true</property>
<!-- validates the table structure as per mapping definition. -->
<property name="hbm2ddl.auto">validate</property>
<!-- Mapping class. -->
<mapping class="com.oyejava.Student"/>
</session-factory>
</hibernate-configuration>
Let's write the Hibernate util class which is a utility class to make the Hibernate session factory. The
Session factory at runtime contains all the configuration and mapping details. From Session factory we
can get the Session, which is a lightweight object and helps us in interacting with the database.
Session can be thought of as a Connection as in JDBC thought the Session may choose to not to open
the connection to the database.
HibernateUtil
public class HibernateUtil {
private static SessionFactory sessionFactory;
static{
try{
//By default it will look for hibernate.cfg.xml in the class path
sessionFactory=new
AnnotationConfiguration().configure().buildSessionFactory();
}catch(Throwable ex){
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory(){
return sessionFactory;
}
public static void shutdown(){
//Close caches and connection pool
getSessionFactory().close();
}
}
Now in the application logic, in this case in the main method we can create a Student object and save
it to the database.
public class HibernateBasic {
public static void main(String[] args) {
//Get the session from the Session factory
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx= session.beginTransaction();
Student student = new Student();
student.setName("oyeJava");
Long studentId = (Long)session.save(student);
System.out.println(studentId);
tx.commit();
session.close();
}
}
Those who have done the hibernate in the XML way will notice that the only difference the two has got
is in terms of where the mapping is kept. You can download the source code from the attachment
section down below.
Hibernate For Entity Beans of EJB
Hibernate also support Entity beans and Java Persistence Provider(JPA) of EJB3.0+ specification. In fact
many of the concepts of Entity beans has been taken from Hibernate. JBoss application server uses
Hibernate as the default persistence provider for Entity beans and JPA. JPA has the notion of
persistence unit which represents the configuration.
EntityManagerFactory of JPA corresponds to SessionFactory and EntityManager corresponds to Session.
Let's do the ((Hibernate Introduction with Annotation)) example of persisting Student object using
Entity beans and JPA API's
Download the Hibernate Core, Hibernate Annotations and Hibernate EntityManager from
https://www.hibernate.org/6.html . Let's make a Java project and pull the following libraries.
From Hibernate Core (<hibernate-distribution-*>/lib/required)
antlr-2.7.6
commons-collections-3.1.jar
dom4j-1.6.1.jar
hibernate3.jar
javassist-3.9.0.GA.jar
jta-1.1.jar
slf4j-api-1.5.8.jar
<hibernate-distribution-*>/lib/ -Choose between cglib or javassist
From Hibernate Annotation:
lib/ejb3-persistence.jar
lib/hibernate-commons-annotations.jar
hibernate-annotations.jar
From Hibernate Entity Manager:
lib/jboss-archive-browsing.jar
hibernate-entitymanager.jar
Download slf4j from http://www.slf4j.org/
slf4j-simple-1.5.8.jar
Hypersonic:
hsqldb.jar - We will be using hypersonic database for this example. you need to download hypersonic jar.
For details see ((Hypersonic)).
Now let's write our domain class which is Student class.Note that it the same class as we did in
((Hibernate Introduction with Annotation))
Student
//Entity annotation represents that it is an entity class and is
//mapped to a table in database. Table annotation tells about the
//table name to which this entity is mapped
@Entity
@Table(name="Student")
public class Student {
//Id will correspond the primary key in the database
private Long id;
protected String name;
//Id - Represents that it is a primary key column
//GeneratedValue - How the key to be generated
//column - Column to which this property is mapped
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name="STUDENT_ID")
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
//There is annotation here so by default it is mapped to
//the column with name NAME. In annotation, the properties are
//by default mapped. In XML mapping by default the columns
//are not mapped.
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Now let's write persistence.xml. This is the configuration file that EJB mandates. It is similar to what
hibernate configuration file does. It can support multiple persistence provider other than hibernate
also.
persistence.xml (Put in META-INF folder under source folder)
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0"
xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<!-- This name is used to refer to this persistence context.
This will be used while we make the EntityManagerFactory -->
<persistence-unit name="hibernateMapping">
<!-- Persistence provider -->
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<!-- Provide properites of persistence provider. This is
specific to each persistence provider. All hibernate related properties
which sit in hibernate.cfg.xml can be put here. -->
<properties>
<property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver"/>
<property name="hibernate.connection.url" value="jdbc:hsqldb:hsql://localhost"/>
<property name="hibernate.connection.username" value="sa"/>
<property name="hibernate.connection.password" value=""/>
</properties>
</persistence-unit>
</persistence>
Now we just have to write our main class and start interacting with database.
public class HibernateMappingJPA {
public static void main(String[] args) {
//The name hibernateMapping comes from persistence.xml
EntityManagerFactory emf =
Persistence.createEntityManagerFactory("hibernateMapping");
//EntityManager is similar to Session in hibernate
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Student student = new Student();
student.setName("James Bond");
em.persist(student);
//Query API
Query query = em.createQuery("Select s from Student s where s.name like ?");
query.setParameter(1, "James%");
List<Student> studentList = query.getResultList();
for(Student s :studentList){
System.out.println(s.getName());
}
tx.commit();
em.close();
}
}
Note that we do not have to map the entity class explicitly as these are scanned automatically based
on the @Entity annotation. The source code can be downloaded from the attachment section down
below.
Hibernate Concepts
Entity and Value Type
Hibernate makes distinction between entity and value types. Entity types represent the objects that
can be persisted to the database. They are the first class citizen in hibernate. Hibernate manages the
life cycle of and entity. An entity also has an identifier which identifies the different instances uniquely.
Value types are objects which are managed in the context of an entity. They live and die within the
scope of an entity. This distinction comes because of Hibernate philosophy of promoting rich domain
model. This means more classes per table. Let's say we have Student Table which contains the
Address also
Student table
ID
NAME
CITY
COUNTRY
Now we can map this table to two class. One Entity and another value type
Student (entity class)
@Entity
public class Student{
private Long id;
private String name;
private Address address;
//Setters and getters
Address (value class)
public class Address{
private String city;
private String country;
//setters and getters
}
So we have got two classes for one table. Also the life of Address object is within the life scope of
Student object.The Student object can live independently but an independent address object has no
meaning unless it is associated with a Student object.
Flushing
Flushing is the process of propagating the changes from the Java layer to the database. Whenever the
call is done for a save, hibernate does not issues the SQL automatically. It keeps on noting the changes
and queues all the SQL. The SQL's are issued to database at appropriate time. This propagation of
changes is known as flushing. The default mode for flushing is automatic, where hibernate identifies
when to flush. The flushing is done in the following cases:
When the transaction is committed.
Before query execution so that the query sees the latest data in the database.
When session.flush() is called explicitly.
The flushing mode can be changed by calling session.setFlushMode with parameter
FlushMode.COMMIT. In this case the flushing happens only
When the transaction is committed.
When session.flush() is called explicitly.
Dirty Checking
Hibernate automatically tracks the changes in the entities when they are in managed state and
persists the changes to the database. Look at the following example
//Get the session from the Session factory. In the first unit of work we persist the Student object
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx= session.beginTransaction();
Student student = new Student();
student.setName("oyeJava");
Long studentId = (Long)session.save(student);
tx.commit();
session.close();
//Start another unit of work. Note that we fetch the same row that we have saved.
//Now change the name of student object but do not call any save or update method.
//Still hibernate will figure out that the object has changed and hibernate will
//persist the changes to the datbase.The process of finding the changes is known as dirty checking
also.
session = HibernateUtil.getSessionFactory().openSession();
tx= session.beginTransaction();
Student student1 = (Student)session.get(Student.class, studentId);
student1.setName("Again Java");
tx.commit();
session.close();
Whenever hibernate fetches or saves an object to the database, it keeps a snapshot of the object. At
the point when hibernate goes to persist the changes, it loops through all the objects and matches it
with the snapshot it has got.If it identifies that the state of an entity has changed from its snapshot it
flushes those changes to the database.
Hibernate Debugging
Debugging Hibernate application tends to be tricky as there is a time lag between application issuing a
calls on hibernate api and hibernate issuing SQL to the database.We will look into various strategies to
look into how to debug hibernate.
Configuration
Enable show_sql property in the hibernate configuration file. In your hibernate.cfg,xml put the
following
<!-- Log out all the sql that hibernate is issuing to datbase.
This is very useful for debugging -->
<property name="show_sql">true</property>
This will flush all the sql that hibernate issues to databae. For example in ((Hibernate Introduction with
Annotation)) example you will see in the console
Hibernate: insert into Student (STUDENT_ID, name) values (null, ?)
Enable format_sql to format the generate sql. However this will take more space. Change in
hibernate.cfg.xml
<!-- Format the logged sql -->
<property name="format_sql">true</property>
In the console
Hibernate:
insert
into
Student
(STUDENT_ID, name)
values
(null, ?)
To understand the origin of SQL, hibernate can put comments which can be enabled by putting the
following property in hibernate.cfg.xml
<!-- Show comment -->
<property name="use_sql_comments">true</property>
In the console
Hibernate:
/* insert com.oyejava.Student */
insert
into
Student
(STUDENT_ID, name)
values
(null, ?)
Enabling log4j
Enabling logging level in log4j.properties can provide more information about the internal of
application. Copy the sample log4j.properties from hibernate core download at etc folder. Run the
application and you will tons of information about internals of hibernate. Tweak the level in
log4j.properties. Enabling log4j.logger.org.hibernate.type at debug level will show the bind
parameters. Do the following in log4j.properties. The following line is already there, you have to
uncomment it.
log4j.logger.org.hibernate.type=TRACE
In the console
Hibernate: insert into Student (STUDENT_ID, name) values (null, ?)
16:04:15,440 DEBUG StringType:133 - binding 'oyeJava' to parameter: 1
Debugging Source
The hibernate download comes with the source code. The src folder in the download has all the source.
For hard to solve problems you can start digging into the source code itself. In eclipse you can do it as
follows:
Go to Project properties by right clicking on project name in navigator view and than click on Properties.
Go to Java Build Path -> Source
Click on Link Source
In the Linked Folder location and pick the location of hibernate source till src
In the folder name give a name which does not conflicts with your present source location.
Click on Finish and than Ok. You can now put break point in the application and can go the hibernate
code.
Hibernate Tools
Hibernate tool comes as an eclipse plugin. This has useful feature including HQL editor which helps in
development. To install Hibernate Tool follow the steps :
Check as per your eclipse version, the right version of tool at http://www.jboss.org/tools/download.html .
You can either download or use the update mechanism. (Update mechanism is better if you are the only
one. If you want to share it among the team members go via download mechanism.) I will follow the
download mechanism to show the steps.
Extract the download zip file. There will be two folders features and plugins. Copy the files inside features
into eclipse/features. Similarly do for plugins.
If eclipse is up, close it and restart.
In eclipse click on Window->Open Perspective->Other.
Click on hibernate. It will open the hibernate perspective.
Hibernate Mapping Class
The basic notion of ORM is mapping between class and table. A class is considered to be an entity
when it gets the Entity annotation.
Entity
@Entity
public class Student {
We can provide a name to an entity. By default the name is the class name. We can give another
name to entity so that conflicts do not occur because of same name entity sitting in different package
@Entity(name="anotherStudent")
public class Student {
By default this entity gets mapped to a table with same name. If we want to specify the table name
explicitly
@Entity
@Table(name="Student")
public class Student {
Dynamic SQL Generation
Hibernate by default generates the SQL for CRUD right in the beginning. This also means that even if
we just update one property of the entity, still the update is issued for all the properties. If we want
hibernate to generate SQL at runtime only taking care of changed entity only than use Entity
annotation coming from hibernate.
Let's introduce a property in Student class age
@Entity
@Table(name="Student")
public class Student {
//Id will correspond the primary key in the database
private Long id;
protected String name;
protected Integer age;
//Getters and Setters
...
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
...
Persist the student by making a new Student object and setting show_sql true.
Student student = new Student();
student.setName("oyeJava");
Long studentId = (Long)session.save(student);
The SQL generated by hibernate as seen in console is
insert into Student (STUDENT_ID, age, name) values (null, ?, ?)
Note that though the age has not been set but still the SQL contains the age parameter. Now let's
introduce the Entity annotation from Hibernate so that we ask hibernate to generate the SQL at
runtime rather than using the pre generated CRUD statement
//dynamicInsert - Include only those properties which are not null in inserting the data
//dynamicUpdate - Include only those properties which have changed
@Entity
@org.hibernate.annotations.Entity(
dynamicInsert=true,
dynamicUpdate=true)
public class Student {
The SQL generated at console is
insert into Student (name, STUDENT_ID) values (?, null)
Note that the age field is absent. This is useful when the table has very large number of columns and
modification and insertion is done only on smaller number of columns.
Immutability
When we know that an entity is not going to change at runtime. For example older messages fetched
from database, than we can ask hibernate to not to do dirty checking for it. This also results in better
performance.
@org.hibernate.annotations.Entity(
dynamicInsert=true,
dynamicUpdate=true,
mutable=false
)
public class Student {
Hibernate Mapping Identity
Identity of an object is the property which is mapped to primary key of the row in the database. The id
of the entity is mapped by @Id annotation
@Entity
@Table(name="Student")
public class Student {
//Id will correspond the primary key in the database
private Long id;
protected String name;
...
//Id - Represents that it is a primary key column
//GeneratedValue - How the key to be generated
//column - Column to which this property is mapped
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name="STUDENT_ID")
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
...
Hibernate supports many identifier generation strategy which supports all the major databases
Hibernate does not allows to change the Id once it has been set. It throws an exception if an attempt is
made
student = new Student();
Long studentId = (Long)session.save(student);
//student.setId(80L); - This will throw an exception
Right now JPA supports limited identifier generation strategy. So if the hibernate generation strategies
need to be employed than do as follows:
@Entity
@org.hibernate.annotations.GenericGenerator(
name="hibernate-native",
strategy="native")
public class Student{
private Long id;
protected String name;
@Id
@GeneratedValue(generator="hibernate-native")
@Column(name="STUDENT_ID")
public Long getId() {
return id;
}
It's possible to write one's own identifier generation strategy by implementing IdentifierGenerator
interface. However try to use one of the existing generator as Hibernate already has very rich identifier
generators.
Hibernate Mapping Properties
In this section we will look into how to map the different types of properties. All properties if mapped
with annotation are by default persistent. This is different from when XML is used for mapping. In XML
mapping, only the properties explicitly mapped are persistent.
Transient
If a property needs to be stopped from being persistent.
@Transient
public int getActive() {
return active;
}
Now this property does not becomes part of persistent mechanism. The property resides only in the
Java layer.
Temporal
Allow to map java.util.Date and java.util.Calender. In SQL the date are differentiated by whether it's a
date, time or a timestamp. Timestamp has both date and time. However in Java the date is always a
timestamp. It has all the information. So we have to deal with this mismatch also.
//Options for TemporalType
// - DATE
// - TIME
// - TIMESTAMP
@Temporal(TemporalType.TIMESTAMP)
public Date getBirthDate() {
return birthDate;
}
Large Data
Large data is characterized by whether it is a binary large data (BLOB) or character larger date (CLOB)
Blob - byte[], Byte[]
Clob - char[], Character[]
BLOB and CLOB both are mapped with Lob annotation
@Lob
public byte[] getPicture() {
return picture;
}
Enumeration
Enumeration are type safe way of restricting the choice in Java. Let's say we have enum defined in
Java as
public enum StudentType {
CURRENT,
ALUMNI
}
to specify the usage of this enum in entity
@Enumerated(EnumType.STRING)
public StudentType getStudentType() {
return studentType;
}
Enum Type can be String or Ordinal. If String is specified, in the database CURRENT or ALUMNI is
stored. If ordinal is specified 0 or 1 is saved. If Enum Type is omitted ORDINAL is taken as default.
Derived Properties
Derived properties are useful to calculate formulas or derived values which is not stored in database
but important for business purpose.
@org.hibernate.annotations.Formula("(MARKS/TOTAL_MARKS)*100")
public Long getPercentage() {
return percentage
}
The value is evaluated while fetching the entity so it might be outdated at other times. It's the
responsibility of the developer to keep it updated.The property appears in the SELECT statement only
and does not participates in INSERT or UPDATE statement.Even SQL expression can be passed to the
underlying database so there is a chance to build database dependency.SQL functions including SQL
subselects can be used.
Handling Generated Values
In DDL we define defaults to many values like SYSDATE for timestamp which means if we do not give
value from Java it is automatically generated.After saving if we want to refresh the object in Java layer,
we will have to call a refresh on the object. The other way is that we can tell Hibernate to refresh the
value automatically after an insertion. Make sure that you have dynamicInsert set to false otherwise
null value will be inserted into the database.
@Column(updatable= false, insertable = false)
@org.hibernate.annotations.Generated
(org.hibernate.annotations.GenerationTime.INSERT)
public Double getPendingAmount() {
return pendingAmount;
}
Embedded classes
Embedded classes are useful for bringing fine domain modeling. We have one table but more classes
mapped to it to bring rich domain model. Though Student table contains all the details for Student and
his address, we will map it to two classes.
Student Table
Student_Id
Name
City
Country
Let's have city and country mapped to a different class Address. We put Embeddable annotation on it
to tell that it will be used as an embedded property in an entity class
@Embeddable
public class Address {
protected String city;
protected String country;
@Column(name = "ADD_CITY")
public String getCity() {
In the Student class
private Address address;
@Embedded
public Address getAddress() {
return address;
}
//Setter method
By default this will mapped to ADD_CITY column name as mapped in Address. To get it mapped to CITY
column as in Student table, we ill have to override the column name.
@Embedded
@AttributeOverride (name= "city",column = @Column(name="CITY"))
public Address getAddress() {
return address;
}
Now in this case we can navigate to Address object by holding a reference of Student object. But it's
not possible to navigate from Address object to Student object. To build the bi directional navigation in
Address entity
In Address class
@org.hibernate.annotations.Parent
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
Hibernate Collection Mapping
Hibernate supports collection mapping as value type. In Hibernate collection mapping, the collection
are mapped into a separate table but are not exposed as entity on the Java side. Hibernate supports
following collection interfaces as value type:
java.util.Set – java.util.HashSet is used to store value.
java.util.SortedSet – java.util.TreeSet is used to store value.
java.util.List – java.util.ArrayList is used to store value. Preserves the position with an index column
Bag semantics – java.util.ArrayList is used to store valre however the position is not preserved.
java.util.Map – java.util.HashMap is used to store value.
java.util.SortedMap – java.util.TreeMap is used to store value.
Let’s say we want to store Phones for a Student. There are more than one Phone for a Student. So we
will have two tables corresponding to Student and Phone. However we do not want to expose Phone
Table STUDENT
STUDENT_ID
...
Table PHONE
STUDENT_ID
NUMBER
...
STUDENT_ID is the foreign key and PHONE table has no primary key. To map the Phone collection as a
Set in the Student Entity class
Student class
...
@org.hibernate.annotations.CollectionOfElements(targetElement = java.lang.String.class)
@JoinTable( name="PHONE",joinColumns = @JoinColumn(name="STUDENT_ID"))
@Column(name="PHONE_NO")
public Set<String> getPhones() {
...
The lifecycle of Phone is tightly coupled to Student. Also Phone table is not exposed as an entity. If the
Set need to be sorted
...
@org.hibernate.annotations.CollectionOfElements(targetElement = java.lang.String.class)
@JoinTable( name="PHONE",joinColumns = @JoinColumn(name="STUDENT_ID"))
@Column(name="PHONE_NO")
@org.hibernate.annotations.Sort(type=org.hibernate.annotations.SortType.NATURAL)
public Set<String> getPhones() {
...
A comparator can also be used for Sort type.
To map the Phone collection as a list
...
@org.hibernate.annotations.CollectionOfElements
@JoinTable( name="PHONE",joinColumns = @JoinColumn(name="STUDENT_ID"))
@org.hibernate.annotations.IndexColumn(name=“INDEX_POSITION", base =1)
@Column(name="PHONE_NO")
public List<String> getPhones() {
return phones;
}
---
Here the index is mapped to a INDEX_POSITION column in the table and preserves the ordering. If the
index is not given, this works like a Bag. Bag is a list but does not preserves the position.
To map Phone as a map where PHONE_NAME will act as key and PHONE_NO as value from the PHONE
table. To map it
...
@org.hibernate.annotations.CollectionOfElements
@JoinTable( name="PHONE",joinColumns = @JoinColumn(name="STUDENT_ID"))
@org.hibernate.annotations.MapKey(columns = @Column(name="PHONE_NAME"))
@Column(name="PHONE_NO")
public Map<String,String> getPhones() {
...
Here the Phone table is mapped directly as a collection. We can also expose Phone as a value object.
In this case define Phone as a class
@Embeddable
public class PhoneValue {
protected Student student;
protected String name;
protected String phoneNumber;
@org.hibernate.annotations.Parent
public Student getStudent() {
return student;
}
...
Also in the above case we maintain a back pointer for Student so that we can navigate from Phone to
Student. To define the collection of embedded object in Student class
...
protected Collection<PhoneValue> phoneValues = new ArrayList<PhoneValue>();
@org.hibernate.annotations.CollectionOfElements
@JoinTable(name = "Student_Phone_Value",joinColumns = @JoinColumn(name="STUDENT_ID"))
@CollectionId(columns= @Column(name="STUDENT_PHONE_VALUE_ID"),
[email protected](type="long"),
generator="sequence")
public Collection<PhoneValue> getPhoneValues() {
return phoneValues;
}
...
Hibernate collection mapping is a way of mapping the collection table as values. The lifecycle of the
the collections are tightly bound to the collection of the owning entity.
Hibernate Relationship Mapping
Hibernate relationship mapping takes care of mapping the relationship in the relational world. The
relationship that exist in the relational world are:
@OneToOne
@OneToMany
@ManyToOne
@ManyToMany
Though the notion of directionality is not present in relational world, it is relevant for the Java world. So
we will have to handle in the Java world whether we want to build unidirectional or bidirectional
relation. Please note the the need of unidirectional or bidirectional relationship should be governed by
your need of domain. For example if we have a Student and Address table with One to One
relationship between them.
One To One
Let's say we have Address and Student in its own table and we expose Address also as an entity.
@Entity
public class AddressEntity implements Serializable{
protected Long id;
….
@Id
@GeneratedValue
@Column(name="ADDRESS_ID")
public Long getId() {
return id;
}
Map the relationship in the Student entity with directionality build from Student side
In Student class
...
private AddressEntity address;
//Cascade Type defines that if the owning entity in this case Student is saved, than
//whether the AddressEntity will also get saved.
@OneToOne(cascade={CascadeType.ALL})
@JoinColumn(name="ADDRESS_ID")
public AddressEntity getAddressEntity() {
return address;
}
...
Also note the cascade type here. The cascade type defines whether the AddressEntity will get saved
when we save Student. For example let's say the mapping in Student class, if we remove the Cascade
type will be:
...
private AddressEntity address;
@OneToOne // comment out - (cascade={CascadeType.ALL})
@JoinColumn(name="ADDRESS_ID")
public AddressEntity getAddressEntity() {
return address;
}
...
Now let's try to save a Student and AddressEntity
Student student = new Student();
AddressEntity add = new AddressEntity();
student.setAddressEntity(add);
//If you try to save student, it will throw an exception as
//it finds AddressEntity wired to it but AddressEntity is transient.
//To persist the transient AddressEntity with the Student uncomment the CascadeType
//session.save(student);
We can make the primary key for both the related table same. It means that in the Address table the
primary key and foreign key are same.
In the Student class
@OneToOne(cascade={CascadeType.ALL})
@PrimaryKeyJoinColumn
public AddressEntity getAddressEntity() {
return addressEntity;
}
In the above case the AddressEntity can be reached from Student but from AddressEntity you cannot
reach to Student. Map the Student entity in the AddressEntity class
...
protected Student student;
@OneToOne(mappedBy="bankAccount")
public Student getStudent() { return student; }
...
Note that this relationship in the Java world does not makes any change to the relational world.
In the case of bidirectional association, there is a notion of owning entity. The owning entity governs in
what case the relationship will materialized in the database.The owning entity is the entity which is on
the opposite or inverse side of mappedBy. In the case as the mappedBy is on the AddressEntity, the
Student entity becomes the owning type. Now look at following piece of code
Student student = new Student();
AddressEntity add = new AddressEntity();
//Map the relationship from AddressEntity side
add.setStudent(student);
//we will assume that CascadeType is All.
session.save(student);
If we look into the database, the Student and AddressEntity both are created in the database but the
relationship is not build between Student and AddressEntity table. To fix the problem change the
above code to
Student student = new Student();
AddressEntity add = new AddressEntity();
//Map the relationship from Student side
student.setAddressEntity(add);
//we will assume that CascadeType is All.
session.save(student);
Now the relationship will be properly created as we have made the relationship from owning side. It's a
good price to build the relationship from both side whenever the relationship is effected from one side.
Student student = new Student();
AddressEntity add = new AddressEntity();
//Good Practice: Map the relationship on both side
student.setAddressEntity(add);
add.setStudent(student);
//we will assume that CascadeType is All.
session.save(student);
For owning entity, you have to take care of following:
If a AddressEntity has to be assigned to a different student, than unset the addressEntity on the original
student and set it into the new student.
For deleting a AddressEntity, unset it from the student object and than remove it from database calling
remove method on entity manager.
One To Many
Let's have PhoneEntity table with one to Many relationship from Student.
Student Table
STUDENT_ID //Primary Key
...
PhoneEntity table
PHONE_ID //Primary Key
STUDENT_ID //Foreign Key
...
Now the PhoneEntity class looks like
@Entity
public class PhoneEntity implements Serializable{
protected int id;
protected String number;
@Id
@GeneratedValue
@Column(name="PHONE_ID")
public int getId() {
There is no reference to Student in PhoneEntity class, though the relationship is maintained in
PhoneEntity table in database. Now to build the One to Many relationship in Student
@OneToMany(cascade={CascadeType.ALL})
public Collection<PhoneEntity> getPhoneEntityList() {
return phoneEntityList;
}
To save phone Entity the code would look like
Collection<PhoneEntity> phoneList = new ArrayList<PhoneEntity>();
PhoneEntity p1 = new PhoneEntity();
p1.setNumber("100");
phoneList.add(p1);
PhoneEntity p2 = new PhoneEntity();
p2.setNumber("200");
phoneList.add(p2);
student.setPhoneEntityList(phoneList);
We can tell hibernate that if an element is removed from a collection than delete it from database as
this is the only reference to that entity.
@OneToMany(cascade={CascadeType.ALL})
@org.hibernate.annotations.Cascade(value=org.hibernate.annotations.CascadeType.DELETE_ORPHAN
)
public Collection<PhoneEntity> getPhoneEntityList() {
return phoneEntityList;
}
If we want the relationship data to be managed in a join table
@OneToMany(cascade={CascadeType.ALL})
@JoinTable(name="STUDENT_PHONE",joinColumns={@JoinColumn(name="STUDENT_ID")},
inverseJoinColumns={@JoinColumn(name="PHONE_ID")})
public Collection<PhoneEntity> getPhoneEntityList() {
return phoneEntityList;
}
Now let's see Many To One unidirectional relationship. Let's say we have a country table and Student is
mapped to Country with Many to One relationship.
Student Table
STUDENT_ID //Primary Key
COUNTRY_ID //foreign Key
...
Country Table
COUNTRY_ID //Primary Key
...
Country Entity
@Entity
public class Country implements Serializable{
protected long id;
protected String name;
@Id
@GeneratedValue
@Column(name="COUNTRY_ID")
public long getId() {
return id;
}
...
Student Entity
private Country country;
@ManyToOne
@JoinColumn(name="COUNTRY_ID")
public Country getCountry() {
return country;
}
Note that we are not using cascade here as normally the country is a master data and should not be
created or deleted in the context of a Student. To save a Student
Country country = new Country();
country.setName("India");
session.save(country);
student.setCountry(country);
Many to one and One to many are two sides of the same relationship. Let’s convert Student and
Country into bidirectional relationship.
//the owning side of relationship is Many To One
@OneToMany(mappedBy="country")
public Collection<Student> getStudentList() {
return studentList;
}
For persistence to work we have to call student.setCountry. If we just call
country.getStudentList().add(student), The relationship will not change in the database. As a good
practice
Always wire both side of relationship.
Many To Many
ManyToMany relationship happens when both side maintains collection based relationship.Let’s take
an example of Student and Language they speak. A student can speak many language. Similarly a
language can be spoken by many students
Language Entity bean
@Entity
public class Language implements Serializable{
protected long id;
protected String name;
Student Bean
@ManyToMany
@JoinTable(name=“A_B",joinColumns={@JoinColumn(name=“C")},
inverseJoinColumns={@JoinColumn(name=“D")})
public Collection<Language> getLanguageList() {
return languageList;
}
For Many to Many bidirectional
Language Bean
@ManyToMany(mappedBy="languageList")
public Collection<Student> getStudentList() {
return studentList;
}
For modifying the same ownership rule applies as we have seen in other bidirectional relationships.
The collection can be fetched in certain order. E.g if we want to fetch the list of languages in certain
order we can say on the Student side of relationship
@ManyToMany
@OrderBy(“name ASC”)
@JoinTable(name="STUDENT_LANGUAGE",
joinColumns={@JoinColumn(name="STUDENT_ID")},
inverseJoinColumns={@JoinColumn(name="LANGUAGE_ID")})
public List<Language> getLanguageList() {
return languageList;
}
For descending order use name=DESC
Hibernate Mapping Inheritance
Inheritance is an important concept in Java but there is no counterpart in Relational world. There are
some solutions is relational world but they are not widely used and also vendor specific. Hibernate
provide some strategies to handle this situation where the inheritance hierarchy is mapped into the
relational world.
Let's have the java classes where we have a User class and Employee and Customer are inherited from
it. The various strategies employed by hibernate to map this hierarchy to relational world is as follows:
Single Table per class hierarchy – Single table has all properties of every class in the hierarchy.
Table per concrete class – Each subclass has a table having all the properties of super class also.
Table per subclass – Each class is mapped in its own table. There is separate table for super class and
subclass.Table
Single Table per class hierarchy
In Single table per subclass, the union of all the properties from the inheritance hierarchy is mapped to
one table. As all the data goes in one table, a discriminator is used to differentiate between different
type of data.
User (Base classe)
//DiscriminatorColumn - Tells about the type of data
//DiscriminatorValue - Is the data is representing User type, the value is "USER"
@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="DISCRIMINATOR", discriminatorType=DiscriminatorType.STRING)
@DiscriminatorValue("USER")
@Table(name="BASIC_USER")
public class User{
protected long id;
protected String name;
Customer class
//There is no id column
@Entity
@DiscriminatorValue("CUST")
public class Customer extends User{
protected double creditLimit;
Employee class
//There is no id column
@Entity
@DiscriminatorValue("EMP")
public class Employee extends User{
protected String rank
...
}
The table structure would look like
Basic_User Table
ID
NAME
CREDIT_LIMIT
RANK
DISCRIMINATOR
Now the objects can be saved polymorphically
//User object referred by User type reference
User user = new User();
user.setName("Superman");
session.save(user);
Customer customer = new Customer();
customer.setName("Spiderman");
customer.setCreditLimit(1060);
//User reference pointing to Customer object
User user2 = customer;
session.save(user2);
Advantages of Single Table per class hierarchy
Simplest to implement.
Only one table to deal with.
Performance wise better than all strategies because no joins or sub-selects need to be performed.
Disadvantages:
Most of the column of table are nullable so the NOT NULL constraint cannot be applied.
Tables are not normalized.
Table per concrete class
In this case let's say our User class is abstract and Customer and Employee are concrete classes. So
the table structure that comes out is basically one table for Customer and one table for Employee. The
data for User is duplicated in both the tables. The User entity in this case is
User
@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public abstract class User{
protected long id;
protected String name;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public long getId() {
...
Customer class
@Entity
public class Customer extends User{
protected double creditLimit;
...
This strategy is not popular and also have been made optional in Java Persistence API.
Advantage:
Possible to define NOT NULL constraints on the table.
Disadvantage:
Tables are not normalized.
To support polymorphism either container has to do multiple trips to database or use SQL UNION kind of
feature.
Table per Sub class
In this case all the classes are mapped to its own table. It's highly normalized but performance is not
good.
User class
@Entity
@Inheritance(strategy=InheritanceType.JOINED)
@Table(name="BASIC_USER")
public class User implements Serializable{
protected long id;
protected String name;
...
}
Employee class
@Entity
@PrimaryKeyJoinColumn(name="EMPLOYEE_ID")
public class Employee extends User{
protected String rank;
...
}
The derived class mapped table contain the foreign key which maps it to the base class. The table
structure is as follows
Basic_User Table
ID
NAME
Employee table
EMPLOYEE_ID // Acts both as primary key and foreign key.
RANK
Advantage:
Tables are normalized.
Able to define NOT NULL constraint.
Disadvantage:
Does not perform as well as SINGLE_TABLE strategy
A comparison of three strategies is as follows:
Criteria Single Table Table per subclassTable per
concrete class
Table Support
Data not normalized.
Constraint for
mandatory columns to
be not nullable cannot
applied.
Change in any
subclass leads to
change in structure of
Table
Normalized.
Mandatory column constraint
can be applied
One table for
each concrete
class.
Not
maintainable.
Change in base
class leads to
changes in all
tables of derived
class
Discriminator
ColumnPresent Absent Absent
Retrieving
data
simple SELECT. All data
is in one table. Using
Joins among table. For example
fetching Customer will require a
Separate Select or
Union Select
discriminator type,
individual types can be
selected
join on Customer and User table.
If all user needs to be fetched
than it will put a join for all three
tables
Updating and
Inserting
Single INSERT or
UPDATE
Multiple. For User type one insert
on User table. For Customer type
one insert on User table and
another on Customer table.
One insert or
update for each
subclass
JPA Support Mandatory Optional
Which Strategy to Choose
Usually the choice is between Single Table and Table per subclass strategy. Table per concrete class is
optional in JPA and may hinder your portability later on.They also represent the two ends of the
spectrum. Some rule of thumbs are:
If there are many properties common in the base class and very less uncommon properties in derived
class. Go for Single table strategy. However make sure your database architect is comfortable with
nullable constraint not put on the properties of derived class. If there are too many uncommon
properties, you might want to go for table per subclass strategy.
Another criteria is the amount of polymorphic queries you do. If most of the time you fetch Customer and
Employees separately you can go for Table per subclass as this will involve join only on two tables a time.
However if you have requirements where you fetch User polymorphically most of the time, Single table
strategy will give better performance.
Inheriting From a non entity base class
Let's say we want to put Audit information in many tables. We can make Audit as a base class and
make our entities class to inherit from Audit class
Audit class
@MappedSuperclass
public class Audit {
protected Date updateDate;
protected Date createDate;
@Temporal(TemporalType.TIMESTAMP)
public Date getCreateDate() {
return createDate;
}
...
}
User class
@Entity
@Table(name="BASIC_USER")
//If we want to overried the column name than AttributeOverride is required
@AttributeOverride(name="createDate",column=@Column(name="CREATE_DATE"))
public class User extends Audit{
...
}
Hibernate Persistent Context and Session
The concept of Persistent Context and Session are central to the runtime behavior of Hibernate.
Persistent Context is a run time memory area where Hibernate holds the references of objects and
Session provides api to interact with the objects. Let's look them into one by one.
Persistent Context
At runtime whenever a session is opened and closed, between those open and close boundaries
Hibernate maintains the object in a Persistence Context. Think of it like a first level runtime cache
which hibernate controls. Hibernate does automatic dirty checking and transactional write-behind for
the entity managed in Persistent Context. Hibernate guarantee a scope of Java object identity in this
cache. Only one object instance representing a particular database row exists in the cache. Hibernate
can extend the persistence context to span a whole conversation.
Let's look at following piece of code. Here you can see the use of Session also.
//Persistent Context 1 Starts
Session session1 = HibernateUtil.getSessionFactory().openSession();
Student studentA = (Student)session1.get(Student.class, studentId);
Student studentB = (Student)session1.get(Student.class, studentId);
if(studentA == studentB){
System.out.println("Objects point to same refrence");
}
session1.close();
//Persistent Context 1 Ends
//Persistent Context 2 Starts
Session session2 = HibernateUtil.getSessionFactory().openSession();
Student studentC = (Student)session2.get(Student.class, studentId);
if(studentA == studentC){
System.out.println("Objects point to same refrence");
}else{
System.out.println("Objects do not point to same reference");
}
session2.close();
//Persistent Context 2 Ends
In the first case studentA and studentB will point to same object as Hibernate guarantees that against
a same id only one object exist in the Persistent context. However in the second case studentA and
studentC will be pointing to different objects as there is a different Persistent context. Hibernate does
not maintains the guarantee beyond a Persistent Context Scope.
Suppose after closing the session2, let's put all the objects in a set
Set allStudents = new HashSet();
allStudents.add(studentA);
allStudents.add(studentB);
allStudents.add(studentC);
The set will only have two objects. The good practice is to avoid using detached object in situation like
above outside of a persistent context or objects coming from different persistent context. Also make
sure that the hashCode() and equals() method are properly overridden.
Important points about Persistent Context:
Beware that hibernate keeps a snapshot of each persistent entity for dirty checking.
Keep the size of your persistent context to minimum. Keep an eye to your sql logs. Avoid object graphs.
Use session.evict(object) to clear big size objects once you are done with it. Use session.clear() when you
want to remove all objects.
Use session.setReadOnly(object,true) you can disable dirty checking for a particular instance.
The biggest problem that comes with ORM solutions are performance problems and memory issues
Session
Hibernate provides api through Session object to handle database interaction task. The database
interaction task are as follows:
Basic CRUD operation
Query Execution
Control of Transaction
Management of Persistent Context
To make an object persistent, save is used
Student tempStudent = new Student();
tempStudent.setName("Om Shanti Om");
session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
session.save(tempStudent);
tx.commit();
session.close();
To retrieve the object, we can use either load or get
session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
//Either use get or load to fetch the entity
student = (Student)session.get(Student.class, studentId);
//student = (Student)session.load(Student.class, studentId);
tx.commit();
session.close();
get() returns null if no database object for the id is found.load() throws ObjectNotFoundException. Also
load method may return a proxy and tries to avoid hitting the database.
session = HibernateUtil.getSessionFactory().openSession();
tx = session.beginTransaction();
Student loadStudent = (Student)session.load(Student.class, studentId);
//The database hit happens here
loadStudent.getName();
tx.commit();
session.close();
Hibernate automatically checks for the changes in the entity and flushes the changes to the database.
session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
student = (Student)session.get(Student.class, studentId);
student.setName("Abhishek");
//No explicit call is made to update the record in database. Still the changes
//are flushed automatically
tx.commit();
session.close();
Data can be removed by calling delete on
Student loadStudent = (Student)session.load(Student.class, studentId);
session.delete(student);
An object has to be loaded in persistence context to use delete. The other option is to use bulk
operations which can issue direct deletes.Hibernate can also roll back the identifier of any entity that
has been deleted, it you enable the hibernate.use_identifier_rollback configuration option. The object
can than be treated as transient object.
After persistence context is closed, item becomes a detached instance. The detached object can be
handled using update or merge.If there is already an instance of object in the persistence context an
exception is thrown.
Managing Detached Entities
Detached entities can be managed again by calling update.
//update to student done outside of persistent scope
student.setName("abc");
session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
//Reattaching. Hibernate will always issue an update,
//even if the object is not dirty
//The update can happens after attaching.
session.update(student);
student.setAddress(…);
tx.commit();
session.close();
The detached entity can be attached using lock
//Modification to a detached entity
session.setName("abc");
session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
//Reattaching. Lockmodes are NONE, READ, UPDATE.
session.lock(student, LockMode.NONE);
//It matters where changes are called. If changes are called before locking only,
//the update is not issued
student.setAddress("Pune");
tx.commit();
session.close();
Merging
Merging merges the detached instance with the managed instance.
session.setName("oyejava");
session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
session.merge(student);
tx.commit();
session.close();
Merging returns a new managed object. The detached instance remains detached. Merging includes all
value typed properties and all additions and removals of elements to any collection. Usually it’s better
to discard the detached instance and continue the work with the managed entity.
Hibernate Entities Life cycle
The entities managed by Hibernate has a life cycle associated with them. Either you can make a new
object in heap and save it into database or the data can be fetched from the database and given to
user as database. The hibernate objects go through certain stages in the life cycle. Only entity objects
participate in the life cycle. The value objects do not participate in the life cycle. The stages in the life
cycle of an entity managed by Hibernate are as follows:
Transient Objects
When objects are created in the Java layer with new or similar operator and they have no
corresponding record existing in database than these objects are termed as Transient objects. These
objects exist in heap. The primary key attribute of this object is still not set. Transient objects are non
transactional and in fact Hibernate has no knowledge of these objects at this point of time, Objects
that are referenced only by other transient instances are, by default also transient. For an instance to
transition from transient to persistent state or to go managed state, it requires either a call to
persistence manger or the creation of a reference from an already persistent instance.
Persistent Objects
Persistent entity is again an object in heap but it has a valid database identity associate with it.
The entity goes into persistent object by one of following means:
Objects instantiated by application and then made persistent by calling one of the methods on the
persistent manager.
The objects become persistent when a reference is created from one of the persistent object.
The entities retrieved from the database either by doing query, lookup or navigating the object graph.
Persistent instances are always associated with a persistent context and Hibernate knows about them
and may track them for dirty checking.
Removed Object
An object scheduled for deletion either by calling delete or because of orphan deletion of entities. One
should not use the object in Java layer as the database record will be deleted at the end of session. A
logic build around these objects might get into trouble.
Detached Object
The object in persistent state go into detached state after the persistent context is closed.
Detached objects can be brought into other persistent context by reattachment or merging. Detached
object still has a valid primary key attribute but it is no longer managed by Hibernate.
Concept of owning entity in EJB and Hibernate
The owning entity concept confuses many of the early adopters of Hibernate and JPA aka Entity beans
in EJB3.0. We often confuse with the notion that we have to tell which of the entity is the owner.
Before we delve deeper into this question let's understand why this whole notion of ownership comes
into picture. The persistence provider gives us a lot of goodies in terms of saving us from handling low
level sql handling and coding in terms of rows and columns. Even we get into this let's move one more
level above and understand that ORM framework provide us a way to think in terms of objects. The
thinking model with which Java programmers are comfortable with. OOAD is a very powerful notion as
it helps us in thinking in terms of real world. ATM, Customer, Item, Bid, Auction become reusable
notions in software world. However to provide that notion to Software world, ORM does a lot of dirty
work (dirty checking !!!) for us.
The most important feature that an ORM provides is transparent persistence. However to provide that
the ORM framework does a lot of work under the hood. For example Hibernate keeps a copy of original
entity from the database and before flushing does a dirty checking (Ahh I have heard this before). It
will compare the values with the working entity and in case of changes will generate the SQL.
Now let's take a concrete example of User and Address both having one to one and bidirectional
relationship. That means we have Address reference from User and User reference from Address. (It
does not matter whether we put the Fk on User side or Address side..Actually it matters with the kind
of Sql the ORM tool will generate to materialize the relationship). Let's say for this example the foreign
key is on User side.
Now as a good Java Programmer whenever we have to change the address of a user we will do
following:'
user.setAddress(add);
add.setUser(add);
If you are an EJB2.1 guy than please look into the EJB3.0. It's more work but with very simple
programming model and high portability.
Now ORM frameworks will do the dirty checking and it will figure out that User and Address both has
changed so it will issue a call to update both address and user.
For User it will be
update user set address_id =?
and for address it will be similar call. So for almost all bidirectional relationship ORM framework will
generate double update calls. This is a performance bottleneck Dr. Watson.
So what we do. We as a programmer tell that which side is the owning, which means for the
relationship to take into effect only one side of the dirty checking for the relationship needs to be
done. If we put inverse or mapped by on address than for the relationship to take effect we need to do
user.setAddress(add);
If we just do add.setUser(add) the change will not be propagated into the database. However as a
good programmer we should wire both side of the relationship.
Now let's think for a moment can ORM framework cannot handle this is some way.Think over it, it can
be done but will be a lot of bookkeeping and will complicate the frameworks a lot complex. Food for
thought!!!
Hibernate Query Language – HQL
Session api's are used when an entity needs to be fetched from the database. To fetch list of records
which might involve joins we have to use Hibernate Query Language (HQL). HQL queries are similar to
SQL. They are written in OO way. Hibernate supports criteria queries also which are type safe way of
writing queries. Hibernate supports the JPA QL also. Hibernate also supports issuing native SQL
queries. The Query interface is used to build and execute queries.
Fetching List
To fetch a list
In HQL:
Query hqlQuery = session.createQuery("Select s from Student s");
List<Student> listStudent = hqlQuery.list();
//Iterating the list
for(Student s: listStudent){
System.out.println(s.getName());
}
Using Criteria:
Criteria crit = session.createCriteria(PhoneEntity.class);
List<PhoneEntity> phones = crit.list();
for(PhoneEntity p: phones){
System.out.println(p.getNumber());
}
To restrict the list based on parameter
HQL way:
hqlQuery = session.createQuery ("from Student s where s.name like :name");
//The wild card can use used as similar to SQL
hqlQuery.setString("name", "ab%");
List<Student> listStudent = hqlQuery.list();
Using Criteria:
Criteria crit = session.createCriteria(Student.class);
crit.add(Restrictions.ilike(“name", “ab”);
List<Student> students = crit.list();
for(Student p: students ){
System.out.println(p.getName());
}
Pagination
Pagination is used when there are huge number of records and we want to fetch a subset of it. For
example we want to display the list of students on front end so we might just want to fetch the 25
records which we want to display in third page. So we want the record which starts at 51th row and
ends at 75th row
HQL way:
Query hqlQuery =
session.createQuery("from Student s order by s.name asc");
hqlQuery.setFirstResult(51);
hqlQuery.setMaxResults(25);
List<Student> listStudent = hqlQuery.list();
Criteria way:
Criteria crit = session.createCriteria(PhoneEntity.class);
crit.addOrder(Order.asc("number"));
crit.setFirstResult(51);
crit.setMaxResults(25);
List<PhoneEntity> phones = crit.list();
Binding Parameters
It's always a good practice to bind the parameters rather than building the queries by String
manipulation. It will protect from SQL injection also. For example do not do like
Select s from Student s where s.name like "James%".
Rather than use parameter binding. The parameters can be bound either using named parameter or
positional parameter. Names parameter is better as it is more maintainable and readable.
Named parameter binding:
hqlQuery = session.createQuery("from Student s where s.name like :name");
//The parameter is given a name
hqlQuery.setString("name", "James%");
List<Student> listStudent = hqlQuery.list();
Positional parameter binding:
hqlQuery = session.createQuery("from Student s where s.name like ?");
//The parameter is referred by position. For more than 1 parameter use number 0,1,2
//as thesequence used in query
hqlQuery.setString0, "James%");
listStudent = hqlQuery.list();
Scrolling with database cursor
Hibernate provides support for JDBC feature called scrollable result where the cursor is held on
database. To open the curosr
ScrollableResults itemCursor = session.createQuery(“from Student s).scroll();
The different functions to iterate using cursor
//Go to first record
itemCursor.first();
//Go to last record
itemCursor.last();
//Go to next record
itemCursor.next();
//Go to previous record
itemCursor.previous();
Named Query
Named query are queries where we give a name to it. These queries can bring performance as they
can be cached.
@NamedQuery{name=“findAllStudents”,
query=“SELECT s from Student s where s.firstName LIKE :firstName”)
Calling the query:
Query query = em.createNamedQuery(“findAllStudents”);
Polymorphic Queries
Hibernate supports polymorphic query. For example suppose we have an inheritance structure on the
OO side as mentioned in ((Hibernate Mapping Inheritance)).It does not matter whichever strategy you
use, Hibernate will ensure to fetch the record as per polymorphism.
If the query is "Select u from User u", hibernate will fetch all the records form User, Customer and
Employee table.
Expressions
Hibernate supports the expression which can narrow down results as per some criteria.
Some example of expression are
//amount between 1 and 10
from Student s where s.pendingAmount between 1 and 10
//amount > 100
from Student s where s.pendingAmount > 100
//Only those students whose email is as mentioned
from Student s where s.email in (‘[email protected]’,’[email protected]’)
//students whose email are not set
where s.email is null
//students whose mails are set
where s.email is not null
//student refers to a phone collection and the collection is not empty
from Student i where i.phones is not empty
Hibernate supports function also
//lowering the name and than comparing
from Student s where lower(s.name) like…
//Concatenating the name and than comparing as the database is having only one name field
//which contains both first and last name together
from Student s where concat(s.firstName,s.lastName) like..
//Student has a collection of phone and has more than 2 phones.
From Student s where size(s.phones) > 2
//current_date will calculate the date and return
Select upper(s.name), current_date() from Student s
Fetching Multiple Objects
Multiple objects can be fetched as an object array.
//It will return an Object [ ] with Student at index 0 and User at index 1
Select s , u from Student s, User u
Scalar queries or Projection
This is very useful in reporting. Fetching a lot of objects when we are looking for only some column
data or want data from different tables leads to performance bottlenecks. In these cases it's better to
use scalar queries as it fetched only required data
Select s.name , s.pendingAmount from Student s
This query will return an Object[].It’s a scalar queries and the data fetched is not associated with
persistence context. The fetched data is not managed for dirty states.
Query Hints
The flush mode can be disabled while executing the query. This can be useful when you do not want
Hibernate to issue a flush before executing the query. For example if before executing the query you
know that the results of the query is not going to be affected by the dirty states of entities in the
session, for performance reason it's better to disable the flush.
//Query way
Query q = session.createQuery(queryString).setFlushMode(FlushMode.COMMIT);
//Criteria way
Criteria c = session.createCriteria(Student.class).setFlushMode(FlushMode.COMMIT);
//JPA way
Query q = em.createQuery(queryString).setFlushMode(FlushModeType.COMMIT);
The cache mode can also be disabled while executing query.It tells hibernate to not to put any entities
in the second level cache
//Query way
Query q = session.createQuery(queryString).setCacheMode(CacheMode.COMMIT);
//Critera way
Criteria c = session.createCriteria(Student.class).setCacheMode(CacheMode.COMMIT);
//In JPA this is not supported as standard.
If hibernate is the JPA provider than it can be used
//using hibernate feature.
Query q = em.createQuery(queryString).setHint
(“org.hibernate.cacheMode”,org.hibernate.CacheMode.IGNORE);
Dirty checking on the fetched objects can also be disabled. When you know that the fetched objects
are not going to change it's better to disable the dirty checking. For example in case of fetching the list
of objects for display purpose only
//Query way
Query q = session.createQuery(queryString).setReadOnly(true);
//Criteria way
Criteria c = session.createCriteria(Student.class).setReadOnly(true);
//In JPA this is not supported as standard.
//If hibernate is the JPA provider than it can be used using hibernate feature.
Query q = em.createQuery(queryString).setHint(“org.hibernate.readOnly”, true);
A timeout can also be passed as query hint which tells how long a long running query can be allowed
//Query way
Query q = session.createQuery(queryString).setTimeout(60);
//Criteria way
Criteria c = session.createCriteria(Student.class).setTimeout(60);
//In JPA this is not supported as standard.
//If hibernate is the JPA provider than it can be used using hibernate feature.
Query q = em.createQuery(queryString).setHint(“org.hibernate.timeout”,60);
Hibernate Shards
Sharding is basically horizontal partitioning of data. If handled carefully, sharding can improve
performance. For example let's say for a online mail provider, the mail subscribers can run into huge
numbers. If all the rows of the database are sitting in the same server, it would hog a huge amount of
memory. Sharding can help in horizontal partitioning of data. We can partition the records on the basic
of continents. The continent information derived from the country information. So the rows belonging
to North America and Asia would be sitting on different servers. If the queries are always continent
specific most of the time, this partitioning will help in improving the performance and also indexing will
be better. The care should be taken that if we have queries which involve going to different databases
to fetch the result set, and if this is a prevalent scenario, sharding would result in degradation of
performance.
Hibernate Shards helps in dealing with horizontal partitioning of data. To use shards, let's assume we
have a EmailUser table where the user data is saved. We will save the records alternately in two
different database and will see that how hibernate shields us from the knowledge that it is talking to
two different database. To make this tutorial, make a java project as outlined in Hibernate Introduction
with Annotation. Also make sure Hypersonic jar is in the path. We will make a tutorial where the data is
saved to two databases. The source code can be downloaded from the attachment section down
below.
Let's make the EmailUser class first. Note that it is a simple mapping class.
@Entity
public class EmailUser {
private BigInteger id;
private String name;
private String continent;
//The id is generated by using a Shard specific generator
@Id
@GeneratedValue(generator="EmailIdGenerator")
@GenericGenerator(name="EmailIdGenerator",
strategy="org.hibernate.shards.id.ShardedUUIDGenerator")
@Column(name="ID")
public BigInteger getId() {
return id;
}
public void setId(BigInteger id) {
this.id = id;
}
//Getters and setters for other properties
...
}
As we are interacting with two different databases, we need to provide two configuration file to
Hibernate. If there are more than two databases, we have to provide same number of configuration
files. Each file representing the details of a database. The first configuration file is usually the master
file, where we provide all the details about other settings. Rest of the configuration files just contains
the details of connection to database and shard specific id's.
hibernate0.cfg.xml (First configuration file)
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-
3.0.dtd">
<hibernate-configuration>
<!-- note the different name -->
<session-factory name="HibernateSessionFactory0">
<!-- Database connection settings -->
<property
name="connection.driver_class">org.hsqldb.jdbcDriver</property>
<property
name="connection.url">jdbc:hsqldb:hsql://localhost</property>
<property name="connection.username">sa</property>
<property name="connection.password"></property>
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">1</property>
<!-- SQL dialect -->
<property name="dialect">org.hibernate.dialect.HSQLDialect</property>
<!-- Enable Hibernate's automatic session context management -->
<property name="current_session_context_class">thread</property>
<!-- Second-level cache -->
<property
name="cache.provider_class">org.hibernate.cache.EhCacheProvider/>
<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>
<!-- property name="format_sql">true</property -->
<!-- property name="use_sql_comments">true</property -->
<!-- Drop and re-create the database schema on startup -->
<property name="hbm2ddl.auto">create-drop</property>
<!-- Shard specific confifuration -->
<property name="hibernate.connection.shard_id">0</property>
<property
name="hibernate.shard.enable_cross_shard_relationship_checks">
true
</property>
</session-factory>
</hibernate-configuration>
The hibernate configuration for second database
hibernate1.cfg (Second Configuration file)
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-
3.0.dtd">
<hibernate-configuration>
<!-- note the different name -->
<session-factory name="HibernateSessionFactory0">
<!-- Database connection settings, this database is in memory -->
<property
name="connection.driver_class">org.hsqldb.jdbcDriver</property>
<property name="connection.url">jdbc:hsqldb:file:1</property>
<property name="connection.username">sa</property>
<property name="connection.password"></property>
<!-- SQL dialect - This tells the SQL grammer to be used -->
<property name="dialect">org.hibernate.dialect.HSQLDialect</property>
<property name="hibernate.connection.shard_id">1</property>
<property
name="hibernate.shard.enable_cross_shard_relationship_checks">
true
</property>
</session-factory>
</hibernate-configuration>
In the true tradition of Hibernate, now we write the HibernateUtil class which is responsible for building
sessionFactory
public class HibernateUtil {
private static SessionFactory sessionFactory;
static{
try{
//Provide the master hibernate config
AnnotationConfiguration prototypeConfig =
new
AnnotationConfiguration().configure("hibernate0.cfg.xml");
prototypeConfig.addAnnotatedClass(EmailUser.class);
List<ShardConfiguration> shardConfigs =
new ArrayList<ShardConfiguration>();
shardConfigs.add(buildShardConfig("hibernate0.cfg.xml"));
shardConfigs.add(buildShardConfig("hibernate1.cfg.xml"));
ShardStrategyFactory shardStrategyFactory =
buildShardStrategyFactory();
ShardedConfiguration shardedConfig =
new ShardedConfiguration(prototypeConfig,
shardConfigs,
shardStrategyFactory);
sessionFactory = shardedConfig.buildShardedSessionFactory();
}catch(Throwable ex){
throw new ExceptionInInitializerError(ex);
}
}
static ShardConfiguration buildShardConfig(String configFile) {
Configuration config = new Configuration().configure(configFile);
return new ConfigurationToShardConfigurationAdapter(config);
}
static ShardStrategyFactory buildShardStrategyFactory() {
ShardStrategyFactory shardStrategyFactory =
new ShardStrategyFactory() {
public ShardStrategy newShardStrategy(List<ShardId>
shardIds) {
RoundRobinShardLoadBalancer loadBalancer =
new RoundRobinShardLoadBalancer(shardIds);
ShardSelectionStrategy shardSelection=
new RoundRobinShardSelectionStrategy(loadBalancer);
ShardResolutionStrategy shardResoultion =
new AllShardsShardResolutionStrategy(shardIds);
ShardAccessStrategy shardAccess =
new SequentialShardAccessStrategy();
return new ShardStrategyImpl(shardSelection,
shardResoultion ,
shardAccess );
}
};
return shardStrategyFactory;
public static SessionFactory getSessionFactory(){
return sessionFactory;
}
public static void shutdown(){
//Close caches and connection pool
getSessionFactory().close();
}
}
Now let's access the session and do database operations
public class HibernateShardsMain {
public static void main(String[] args){
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx= session.beginTransaction();
EmailUser e1= new EmailUser();
e1.setName("abc");
e1.setContinent("North America");
System.out.println(session.save(e1));
EmailUser e2= new EmailUser();
e2.setName("def");
e2.setContinent("Asia");
System.out.println(session.save(e2));
EmailUser e3= new EmailUser();
e3.setName("fgh");
e3.setContinent("Asia");
System.out.println(session.save(e3));
EmailUser e4= new EmailUser();
e4.setName("ijk");
e4.setContinent("North America");
System.out.println(session.save(e4));
tx.commit();
session.close();
session = HibernateUtil.getSessionFactory().openSession();
tx= session.beginTransaction();
Criteria crit = session.createCriteria(EmailUser.class);
crit.add(Restrictions.ilike("continent", "North America"));
crit.list();
tx.commit();
session.close();
}
}
Note that the main code has no knowledge that it is working against multiple databases. It looks
similar to the code as we see while we interact with a single database. As we are using a round robin
strategy, Hibernate will save the data alternately in two different databases.
Before you plan to use Hibernate shards, be careful of following points:
It looks like Shards in not under active developmnet as the last checkin is of 2007.
Shards has serious problems in dealing with queries, especially order by and distinct. These problems are
itself very complex when we are delaing in a sharded environment. For the order by and distinct to work,
we have to bring the data from all shards at a common location and than build the result. But than we
run inot book keeping and memory issues.
Hibernate - Maintaining a Long Session
Sometimes we need to maintain a long running session. For example, we need to do some
intermediate calculation and keep track of them. The user might at the end chose to discard it or
retain it. For the same purpose, we might want to keep a long running session in the Hibernate.
Another way is to keep a long running transaction, but many times the transaction times out because
of the underlying database. Also be careful about the issues of entities being updated by other
threads, which are more about versioning issues. This can lead to commit getting failed at the end, if
versioning is enabled for the entities.
The way to handle this is usually to open a hibernate session at the start of workflow and then keep
the hibernate session in the Http session object. Whenever next requests comes up, the hibernate
session is tied to the current thread and is used by all the entities related calls. At the end, one can
choose to commit the changes or discard it.
To start with, assume that you have identified that we are in the context of a workflow . So that means
we need to create a new session or if the session is already created then bind to it.
//Let's say we do it in filter and based on certain attribute we know we
are in Workflow. WorkflowHandler
//can be an interface, which can be used to designate the wrokflow related
things
if (/*In the context of workflow */) {
HttpSession httpSession = //get the session
//check if we have already have the entity manager created
EntityManager entityManager = (EntityManager)
httpSession.getAttribute("EXTENDED_PERSISTENCE_CONTEXT");
if (entityManager != null) {
//put this in the current thread
} else {
//create a new persistence context
//emf is entity manager factory, which can be
injected. Also
//make sure to make the flush mode as manual
EntityManager em = emf.createEntityManager();
((Session)
em.getDelegate()).setFlushMode(FlushMode.MANUAL);
}
}
Also when the request is processed and just before returning back to client
if (/* in context of workflow */) {
HttpSession httpSession =//get session
EntityManager entityManager = //get the entity manger
//If the entity manager is open, then the end condition has not reached,
so save it in session
if (entityManager != null && entityManager.isOpen()) {
ManagedSessionContext.unbind(((Session)
entityManager.getDelegate()).getSessionFactory());
httpSession.setAttribute("EXTENDED_PERSISTENCE_CONTEXT",
<pass the instance of persistence context>);
} else {
//If we are at end, then remove the attribute
httpSession.removeAttribute("EXTENDED_PERSISTENCE_CONTEXT");
}
}
Then in the intermediate step of operations, open a transaction and do the work.
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
Session session = (Session) entityManager.getDelegate();
//Do the work here.
tx.commit();
If all the changes has to be committed at the end, then do
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
entityManager.flush();
tx.commit();
entityManager.close();
entityManager
And if it needs to be rolled back
entityManager.clear();
entityManager.close();
Hibernate Second level Cache
Hibernate second level cache are important for performance perspective. A second level cache is a
process specific cache and is shared by all the individual session running in different threads. The data
points which are good candidates for caching are:
Data which never change, like list of countries, timezone,role types etc.
Data that rarely change. (A more changing data in cache will lead to Hibernate doing more work as it has
to write to database and has to take care of objects in cache).
To enable the cahce first we need to enable some properties in configuration. I am showing an
example for properties as in Spring environment but same can be adapted for any other environment
like as in persistence.xml
In Spring:
<!-- Enabling second level cache for hibernate -->
<prop key="hibernate.cache.provider_class">net.sf.ehcache.hibernate.EhCacheProvider</prop>
<!-- prop
key="hibernate.cache.region.factory_class">net.sf.ehcache.hibernate.EhCacheRegionFactory</prop --
>
<!-- prop
key="hibernate.cache.region.factory_class">net.sf.ehcache.hibernate.SingletonEhCacheRegionFactory
</prop -->
<prop key="hibernate.cache.provider_configuration_file_resource_path">ehcache.xml</prop>
<prop key="hibernate.cache.use_query_cache">true</prop>
<prop key="hibernate.cache.use_second_level_cache">true</prop>
In persistence.xml
<!-- Enabling second level cache for hibernate -->
<property name="hibernate.cache.provider_class" value="net.sf.ehcache.hibernate.EhCacheProvider"
/>
<!-- prop key="hibernate.cache.region.factory_class"
value="net.sf.ehcache.hibernate.EhCacheRegionFactory/ -->
<!-- prop key="hibernate.cache.region.factory_class"
value="net.sf.ehcache.hibernate.SingletonEhCacheRegionFactory/ -->
<property name="hibernate.cache.provider_configuration_file_resource_path"
value="ehcache.xml" />
<property name="hibernate.cache.use_query_cache" value="true"/>
<property name="hibernate.cache.use_second_level_cache " value="true"/>
Note the commented secion of cache provider. The commented secion is as per EhCache document
but I could not get it work.
After that a configuration XML need to be provided in the classpath, which in this example is
ehcache.xml. A sample ehcache.xml looks like
<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
<!-- Required elements -->
<diskStore path="java.io.tmpdir" />
<defaultCache maxElementsInMemory="25000" eternal="false"
timeToIdleSeconds="300" timeToLiveSeconds="300" overflowToDisk="true"
diskPersistent="false" diskExpiryThreadIntervalSeconds="300"
memoryStoreEvictionPolicy="LRU" />
<cache name="com.lalit.domain.Role" eternal="true"
maxElementsInMemory="10" />
</ehcache>
Here we are hooking Role object to be put in cache. The eternal is true as we do not expect role object
to ever change so we can keep it always in cache. This might change based on your requirements.
Now the last part is to tell the Role object that it needs to participate in cache. For that
@Entity
@Table(name = "app_roles")
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
public class Role{
You might want to change your CacheConcurrencyStretegy based on your need. To make sure that the
Cache is working fine, run the app with hibernate logging level at DEBUG. And you should be able to
see logging statements like "Cache hit".
Happy caching.