Session Object States in Hibernate
Session Object States
Hibernate is a full object/relational mapping solution that not only shields the developer from the details of the underlying database management system, but also offers state management of objects.
The Hibernate application developers should always think about the state of their objects, and not necessarily about the execution of SQL statements. This part is taken care of by Hibernate and is only relevant for the application developer when tuning the performance of the system.
Hibernate Session
The main runtime interface between a Java application and Hibernate. This is the central API class abstracting the notion of a persistence service. The lifecycle of a Session is bounded by the beginning and end of a logical transaction. (Long transactions might span several database transactions.).
A Session is used to get a physical connection with a database. The Session object is lightweight and designed to be instantiated each time an interaction is needed with the database. Persistent objects are saved and retrieved through a Session object.
The main function of the Session is to offer create, read and delete operations for instances of mapped entity classes. Instances may exist in one of three states:
transient: never persistent, not associated with any Session
persistent: associated with a unique Session
detached: previously persistent, not associated with any Session.
The session objects should not be kept open for a long time because they are not usually thread safe and they should be created and destroyed them as needed. The main function of the Session is to offer, create, read, and delete operations for instances of mapped entity classes.
Student.java
@Entity@Table(name = "student")
public class Student {
@Id@GeneratedValue@Column(name = "roll")
private long roll;
@Column(name = "name", nullable = false, length = 100)
private String name;
//@Getters @Setters Constructors
}
HibernateUtil.java
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
public class HibernateUtil {
private static final SessionFactory sessionFactory;
static {
try {
sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
} catch(Throwable ex) {
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}
1). Transient
An object is transient if it has just been instantiated using the new operator, and it is not associated with a Hibernate Session. It has no persistent representation in the database and no identifier value has been assigned.
Transient instances will be destroyed by the garbage collector if the application does not hold a reference anymore. Use the Hibernate Session to make an object persistent (and let Hibernate take care of the SQL statements that need to be executed for this transition).
Session session = HibernateUtil.getSessionFactory().openSession();
Student student = new Student(111,"Sara");
Transient instances may be made persistent by calling save(), persist() or saveOrUpdate(). Persistent instances may be made transient by calling delete(). Any instance returned by a get() or load() method is persistent.
Detached instances may be made persistent by calling update(), saveOrUpdate(), lock() or replicate(). The state of a transient or detached instance may also be made persistent as a new persistent instance by calling merge().
2). Persistent
A persistent instance has a representation in the database and an identifier value. It might just have been saved or loaded, however, it is by definition in the scope of a Session.
Hibernate will detect any changes made to an object in persistent state and synchronize the state with the database when the unit of work completes. Developers do not execute manual UPDATE statements, or DELETE statements when an object should be made transient.
Session session = HibernateUtil.getSessionFactory().openSession();
Student student = new Student(111,"Sara");
session.persist(student);
save() and persist() result in an SQL INSERT, delete() in an SQL DELETE and update() or merge() in an SQL UPDATE. Changes to persistent instances are detected at flush time and also result in an SQL UPDATE. saveOrUpdate() and replicate() result in either an INSERT or an UPDATE.
3). Detached
A detached instance is an object that has been persistent, but its Session has been closed. The reference to the object is still valid, of course, and the detached instance might even be modified in this state.
A detached instance can be reattached to a new Session at a later point in time, making it (and all the modifications) persistent again.
This feature enables a programming model for long running units of work that require user think-time. We call them application transactions, i.e., a unit of work from the point of view of the user.
Session session = HibernateUtil.getSessionFactory().openSession();
Student student = new Student(111,"Sara");
session.persist(student);
session.close();
Loading an object
The load() methods of Session provide a way of retrieving a persistent instance if you know its identifier. load() takes a class object and loads the state into a newly instantiated instance of that class in a persistent state.
Session session = HibernateUtil.getSessionFactory().openSession();
Student student = (Student) session.load(new Long(111) , Student.class);
Be aware that load() will throw an unrecoverable exception if there is no matching database row. If the class is mapped with a proxy, load() just returns an uninitialized proxy and does not actually hit the database until you invoke a method of the proxy.
This is useful if you wish to create an association to an object without actually loading it from the database. It also allows multiple instances to be loaded as a batch if batch-size is defined for the class mapping.
If you are not certain that a matching row exists, you should use the get() method which hits the database immediately and returns null if there is no matching row.
Student student = (Student) session.get(Student.class, id);
if (student==null) {
student = new Student();
session.save(student, id);
}
return student;
You can even load an object using an SQL SELECT ... FOR UPDATE, using a LockMode.
Student student = (Student) session.get(Student.class, id, LockMode.UPGRADE);
Any associated instances or contained collections will not be selected FOR UPDATE, unless you decide to specify lock or all as a cascade style for the association.
It is possible to re-load an object and all its collections at any time, using the refresh() method. This is useful when database triggers are used to initialize some of the properties of the object.
session.save(student);
session.flush(); //force the SQL INSERT
session.refresh(student); //re-read the state (after the trigger executes)
Modifying persistent objects
Transactional persistent instances (i.e. objects loaded, saved, created or queried by the Session) can be manipulated by the application, and any changes to persistent state will be persisted when the Session is flushed.
There is no need to call a particular method (like update(), which has a different purpose) to make your modifications persistent. The most straightforward way to update the state of an object is to load() it and then manipulate it directly while the Session is open:
Session session = HibernateUtil.getSessionFactory().openSession();
Student student = (Student) session.load(new Long(111) , Student.class);
student.setPhone("1234567890");
session.flush(); // changes to student are automatically detected and persisted
Sometimes this programming model is inefficient, as it requires in the same session both an SQL SELECT to load an object and an SQL UPDATE to persist its updated state. Hibernate offers an alternate approach by using detached instances.
Modifying detached objects
Many applications need to retrieve an object in one transaction, send it to the UI layer for manipulation, then save the changes in a new transaction. Applications that use this kind of approach in a high-concurrency environment usually use versioned data to ensure isolation for the "long" unit of work.
Hibernate supports this model by providing for reattachment of detached instances using the Session.update() or Session.merge() methods:
// in the first session
Session firstSession = HibernateUtil.getSessionFactory().openSession();
Student student1 = (Student) session.load(new Long(111) , Student.class);
Student student2 = new Student();
firstSession.save(student2);
// in a higher layer of the application
student1.setPhone("0987654321");
// later, in a new session
Session secondSession = HibernateUtil.getSessionFactory().openSession();
secondSession.update(student1); // update student1
secondSession.update(student2); // update student2
If the Student with identifier catId had already been loaded by secondSession when the application tried to reattach it, an exception would have been thrown.
Use update() if you are certain that the session does not contain an already persistent instance with the same identifier. Use merge() if you want to merge your modifications at any time without consideration of the state of the session.
In other words, update() is usually the first method you would call in a fresh session, ensuring that the reattachment of your detached instances is the first operation that is executed.
In this article, we have seen Session Object States in Hibernate .
0 Comments
Post a Comment