Monday, July 28, 2014

Hibernate Persistent Context and Session

The concept of Persistent Context and Session are central to the runtime behavior of Hibernate. Persistent Context is a run time memory area where Hibernate holds the references of objects and Session provides API to interact with the objects. Let's look them into one by one.
Persistent Context
At runtime whenever a session is opened and closed, between those open and close boundaries Hibernate maintains the object in a Persistence Context. Think of it like a first level runtime cache which hibernate controls. Hibernate does automatic dirty checking and transactional write-behind for the entity managed in Persistent Context. Hibernate guarantee a scope of Java object identity in this
cache. Only one object instance representing a particular database row exists in the cache. Hibernate can extend the persistence context to span a whole conversation.
Let's look at following piece of code. Here you can see the use of Session also.
//Persistent Context 1 Starts
Session session1 = HibernateUtil.getSessionFactory().openSession();

Student studentA = (Student)session1.get(Student.class, studentId);

Student studentB = (Student)session1.get(Student.class, studentId);

if(studentA == studentB){

    System.out.println("Objects point to same refrence");

}

session1.close();

//Persistent Context 1 Ends

//Persistent Context 2 Starts


Session session2 = HibernateUtil.getSessionFactory().openSession();

Student studentC = (Student)session2.get(Student.class, studentId);

if(studentA == studentC){

    System.out.println("Objects point to same refrence");

}else{

    System.out.println("Objects do not point to same reference");

}     

//Persistent Context 2 Ends   

session2.close();


In the first case studentA and studentB will point to same object as Hibernate guarantees that against a same id only one object exist in the Persistent context. However in the second case studentA and studentC will be pointing to different objects as there is a different Persistent context. Hibernate does not maintains the guarantee beyond a Persistent Context Scope.

Suppose after closing the session2, let's put all the objects in a set
Set allStudents = new HashSet();
allStudents.add(studentA);

allStudents.add(studentB);

allStudents.add(studentC);

The set will only have two objects. The good practice is to avoid using detached object in situation like above outside of a persistent context or objects coming from different persistent context. Also make sure that the hashCode() and equals() method are properly overridden.
Important points about Persistent Context:
  • Beware that hibernate keeps a snapshot of each persistent entity for dirty checking.
  • Keep the size of your persistent context to minimum. Keep an eye to your SQL logs. Avoid object graphs.
  • Use session.evict(object) to clear big size objects once you are done with it. Use session.clear() when you want to remove all objects.
  • Use session.setReadOnly(object,true) you can disable dirty checking for a particular instance.
The biggest problem that comes with ORM solutions are performance problems and memory issues

Video explaining Session and Persistent Context


Session
Hibernate provides API through Session object to handle database interaction task. The database interaction task are as follows:
  • Basic CRUD operation
  • Query Execution
  • Control of Transaction
  • Management of Persistent Context
To make an object persistent, save is used
Student tempStudent = new Student();
tempStudent.setName("Om Shanti Om");        

session = HibernateUtil.getSessionFactory().openSession();

Transaction tx = session.beginTransaction();

session.save(tempStudent);

tx.commit();

session.close();

To retrieve the object, we can use either load or get
session = HibernateUtil.getSessionFactory().openSession();


Transaction tx = session.beginTransaction();

//Either use get or load to fetch the entity
student = (Student)session.get(Student.class, studentId);

//student = (Student)session.load(Student.class, studentId);

tx.commit();


session.close();

get() returns null if no database object for the id is found.load() throws ObjectNotFoundException. Also load method may return a proxy and tries to avoid hitting the database.
session = HibernateUtil.getSessionFactory().openSession();


tx = session.beginTransaction();

Student loadStudent = 
           (Student)session.load(Student.class, studentId);


//The database hit happens here

loadStudent.getName();

tx.commit();


session.close();


Hibernate automatically checks for the changes in the entity and flushes the changes to the database.
session = HibernateUtil.getSessionFactory().openSession();


Transaction tx = session.beginTransaction();

student = (Student)session.get(Student.class, studentId);


student.setName("Abhishek");

//No explicit call is made to update the record in database. Still the changes

//are flushed automatically

tx.commit();


session.close();

Data can be removed by calling delete on
Student loadStudent = (Student)session.load(Student.class, studentId);


session.delete(student);

An object has to be loaded in persistence context to use delete. The other option is to use bulk operations which can issue direct deletes.Hibernate can also roll back the identifier of any entity that has been deleted, it you enable the hibernate.use_identifier_rollback configuration option. The object can than be treated as transient object.
After persistence context is closed, item becomes a detached instance. The detached object can be handled using update or merge.If there is already an instance of object in the persistence context an exception is thrown.
Managing Detached Entities
Detached entities can be managed again by calling update.
//update to student done outside of persistent scope


student.setName("abc");

session = HibernateUtil.getSessionFactory().openSession();


Transaction tx = session.beginTransaction();

//Reattaching. Hibernate will always issue an update,

//even if the object is not dirty

//The update can happens after attaching.


session.update(student);

student.setAddress(…);        

tx.commit();

session.close();

The detached entity can be attached using lock
//Modification to a detached entity


session.setName("abc");

session = HibernateUtil.getSessionFactory().openSession();


Transaction tx = session.beginTransaction();

//Reattaching. Lockmodes are NONE, READ, UPDATE. 


session.lock(student, LockMode.NONE);

//It matters where changes are called. If changes are called before locking only,


//the update is not issued 

student.setAddress("Pune");        

tx.commit();

session.close();

Merging
Merging merges the detached instance with the managed instance.
session.setName("oyejava");


session = HibernateUtil.getSessionFactory().openSession();

Transaction tx = session.beginTransaction();

session.merge(student);                
tx.commit();


session.close();

Merging returns a new managed object. The detached instance remains detached. Merging includes all value typed properties and all additions and removals of elements to any collection. Usually it’s better to discard the detached instance and continue the work with the managed entity.

1 comment:

  1. Nice tutorial. Please explain the CRUD operations diagramatically

    ReplyDelete