object relationship mapping and hibernate

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

Upload: joe-jacob

Post on 10-May-2015

968 views

Category:

Technology


3 download

TRANSCRIPT

Page 1: Object relationship mapping and hibernate

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

Page 2: Object relationship mapping and hibernate

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

Page 3: Object relationship mapping and hibernate

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

Page 4: Object relationship mapping and hibernate

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

Page 5: Object relationship mapping and hibernate

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

Page 6: Object relationship mapping and hibernate

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)

Page 7: Object relationship mapping and hibernate

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)

Page 8: Object relationship mapping and hibernate

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

Page 9: Object relationship mapping and hibernate

<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

Page 10: Object relationship mapping and hibernate

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();

Page 11: Object relationship mapping and hibernate

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)

Page 12: Object relationship mapping and hibernate

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;

Page 13: Object relationship mapping and hibernate

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

Page 14: Object relationship mapping and hibernate

<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

Page 15: Object relationship mapping and hibernate

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();

Page 16: Object relationship mapping and hibernate

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

Page 17: Object relationship mapping and hibernate

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;

Page 18: Object relationship mapping and hibernate

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

Page 19: Object relationship mapping and hibernate

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

Page 20: Object relationship mapping and hibernate

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

Page 21: Object relationship mapping and hibernate

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

Page 22: Object relationship mapping and hibernate

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

Page 23: Object relationship mapping and hibernate

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, ?)

Page 24: Object relationship mapping and hibernate

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:

Page 25: Object relationship mapping and hibernate

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

Page 26: Object relationship mapping and hibernate

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

}

...

Page 27: Object relationship mapping and hibernate

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,

Page 28: Object relationship mapping and hibernate

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

Page 29: Object relationship mapping and hibernate

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

Page 30: Object relationship mapping and hibernate

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;

}

Page 31: Object relationship mapping and hibernate

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

Page 32: Object relationship mapping and hibernate

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

Page 33: Object relationship mapping and hibernate

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.

Page 34: Object relationship mapping and hibernate

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

Page 35: Object relationship mapping and hibernate

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

Page 36: Object relationship mapping and hibernate

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

Page 37: Object relationship mapping and hibernate

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

}

...

Page 38: Object relationship mapping and hibernate

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

Page 39: Object relationship mapping and hibernate

...

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.

Page 40: Object relationship mapping and hibernate

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;

Page 41: Object relationship mapping and hibernate

@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

Page 42: Object relationship mapping and hibernate

@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

Page 43: Object relationship mapping and hibernate

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

Page 44: Object relationship mapping and hibernate

@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

Page 45: Object relationship mapping and hibernate

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

Page 46: Object relationship mapping and hibernate

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

Page 47: Object relationship mapping and hibernate

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:

Page 48: Object relationship mapping and hibernate

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

Page 49: Object relationship mapping and hibernate

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

Page 50: Object relationship mapping and hibernate

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;

Page 51: Object relationship mapping and hibernate

@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);

Page 52: Object relationship mapping and hibernate

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.

Page 53: Object relationship mapping and hibernate

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();

Page 54: Object relationship mapping and hibernate

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.

Page 55: Object relationship mapping and hibernate

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();

Page 56: Object relationship mapping and hibernate

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

Page 57: Object relationship mapping and hibernate

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

Page 58: Object relationship mapping and hibernate

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

Page 59: Object relationship mapping and hibernate

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());

}

Page 60: Object relationship mapping and hibernate

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:

Page 61: Object relationship mapping and hibernate

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

Page 62: Object relationship mapping and hibernate

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

Page 63: Object relationship mapping and hibernate

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.

Page 64: Object relationship mapping and hibernate

//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);

Page 65: Object relationship mapping and hibernate

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

Page 66: Object relationship mapping and hibernate

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;

}

Page 67: Object relationship mapping and hibernate

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>

Page 68: Object relationship mapping and hibernate

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

Page 69: Object relationship mapping and hibernate

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

Page 70: Object relationship mapping and hibernate

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

}

Page 71: Object relationship mapping and hibernate

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

Page 72: Object relationship mapping and hibernate

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();

Page 73: Object relationship mapping and hibernate

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

Page 74: Object relationship mapping and 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

Page 75: Object relationship mapping and hibernate

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

Page 76: Object relationship mapping and hibernate

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

Page 77: Object relationship mapping and hibernate

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

Page 78: Object relationship mapping and hibernate

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.