Friday, July 31, 2009

Object states in hibernate

Object states in hibernate:
An object in a hibernate can be in one of the following states:

1. Transient
2. Persistent
3. Detached

1. Transient: An object which is just instantiated using the "new" operator but not associated with any session in hibernate is said to be in Transient state. In other words, a transient object doesnot have any existing representation in the database .
Ex: User user = new User(); //Here, we just instantiated but did not associated it with the session. So, it user is said to be in transient state.


2. Persistent: An object is said to be in persistent state under the following scenarios:
a) It is associated with a hibernate session in some way like get, load, save, saveOrUpdate etc and is in the scope of an active transaction within that session.
ib) It has not been manually detached or evicted from the session either by calling evict on session (or) by closing the session (or) by ending the transaction through commit() or rollback()
Ex:
i) User user = new User();
ii) Session session = getSessionFactory().openSession();
iii) Transaction tx = session.beginTransaction();
iv) user = (User) session.get(User.class, new Long(1));
v) user.setName("Varun");
vi) session.saveOrUpdate(user);
vii) user.setEmail("varun@gmail.com");
viii) user.setJob("Software Engineer");
ix) tx.commit();


In the above example, the user object will be in transient state in step (i) and will continue to be in that state till step (iii). However, at step (iv), the user object is associated with a hibernate session through session.get(...) method. It is at this point of time, that the object of user instance is said move from 'transient' to 'persistent' state and it will continue to remain in that state till the last step i.e. step (ix) where the transaction is committed. One important thing to remember for hibernate's persistent state is that once an object is in persistent state (i.e. associated with the session), all the changes made to that instance are commited in the database when we commit the transaction. There is no need to call save or saveOrUpdate whenever we change few properties of that instance if it is still associated within a transaction. So, even though we called saveOrUpdate at step (v), but when the tx is committed at step (ix), we would find the user's email and job also saved with the values we supplied at steps (vii) and (viii)

3. Detached: An object or instance is said to be in 'detached' when it is no longer associated with a hibernate session or no mechanism exist to tie the instance to the database. This happens in either of the following conditions:
a) transaction is commited
b) session is closed
c) we programatically detach the instance by calling session.evict and passing the instance to it.
Furthermore, if you have any instances in scope while the transaction failed, those instances will become detached instances, because the transaction and the Hibernate Session with which they were associated are junked, and no longer capable of persisting the state of your data.
Points a and b from above are self explainatory. For example on programatically detaching the instance from the session, consider the following scenario:
User u = new User();
Session session = getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
user = (User) session.load(User.class, new Long(1));
user.setEmail("varun@gmail.com");
session.save(user);
user.setEmail("abc@gmail.com");
session.save(user);
session.evict(user);
user.setEmail("def@gmail.com");
tx.commit();

In the above example, the value of the user email would be set in database as "abc@gmail.com". Since after this, we have detached the user from the session, the session will no longer have a user associated with it. It has moved to detached instance. And once a user is detached, then, even if we change some of its values and commit the transaction under which it is runnning, those values wont be persisted to the database since the transaction just checks for the instances which are associated with the session under which it began.

If the above example is changed to:

user.setEmail("varun@gmail.com");
session.save(user);
user.setEmail("abc@gmail.com");
session.save(user);
user.setEmail("def@gmail.com");
tx.commit();
user.setEmail("ghi@gmail.com");
session.save(user);


Then, the value of the user's email property would be "def@gmail.com". This is because the instance gets detached automatically when the transaction which is opened under a session is committed.
One important rule Hibernate strictly enforces: only one instance of a class with a given unique primary key can be associated with the Session at a time. In the below shown example, if we try to re-associate the evicted user1 with the Hibernate Session, Hibernate will kick out to us a NonUniqueObjectException, indicating that it is already managing an instance that represents that particular database record.
User user1 = new User();
user1.setPassword("aaaaaa");
Long id = (Long)session.save(user1);
session.evict(user1);
User user2 = (User)session.get(User.class, id);
user1.setVerified(true);
session.saveOrUpdate(user1);
HibernateUtil.commitTransaction();