I am new to hibernate. I would like to know if we have any alternatives for session.evict().
I have commented that line and my logic works fine.
session.evict() method is used to remove a particular object from cache associated with the session. So removing it will force the hibernate to fetch the object from database instead of looking into cache. Example
Session session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
try
{
SomeEntity myEntity= (SomeEntity) session.load(SomeEntity.class, new Integer(1)); //look in cache (will not be found) if not found then fetch from database
System.out.println(myEntity.getName());
myEntity = (SomeEntity) session.load(SomeEntity.class, new Integer(1)); //look in cache(will be found) if not then fetch from database
System.out.println(myEntity.getName());
session.evict(myEntity); // will remove from cache
myEntity = (SomeEntity) session.load(SomeEntity.class, new Integer(1)); // object will again be fetched from database if not found in second level cache
System.out.println(myEntity.getName());
}
finally
{
session.getTransaction().commit();
HibernateUtil.shutdown();
}
Edit : Session.evict() will remove the entity from first level cache and after removing the object from the session, any change to object will not be persisted. The associated objects will also be detached if the association is mapped with cascade="evict".
Hope it helps!!
It's well explained in blogs such as [1] that update() and merge() methods may behave differently in case we need to update some detached entity. On the other hand, blog [2] states:
First level cache is associated with “session” object and other session objects in application can not see it.The scope of cache objects is of session. Once session is closed, cached objects are gone forever.
Here's a code snippet from reference [1]:
Session session = factory.openSession();
Student student = (Student) session.get(Student.class, 111);
session.close();
student.setName("chandrashekhar");
Session session2 = factory.openSession();
Student student2 = session2.get(Student.class, 111); //a new Student object with id=111
Transaction tx = session2.beginTransaction();
session2.update(student); //throws NonUniqueObjectExcpetion because there's already *student2* in session2 cache
tx.commit();
Here's my question: session and session2 are two different Session objects. why does update() method throw a NonUniqueObjectExcpetion while by closing the object session it should've destroyed the object student? (thus we couldn't have encountered such an exception.)
While learning HIbernate I came across this in the Hibernate official documentation:
That is because Hibernate caches all the newly inserted Customer instances in the session-level cache.
I am aware that Hibernate caches the entities retrieved but does it cache the new ones as well?
EDIT: newly created instance like session.save(new Customer())
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
for ( int i=0; i<100000; i++ ) {
Customer customer = new Customer(.....);
session.save(customer);
}
tx.commit();
session.close();
What the cache here means that after session.save(customer) , the customer would still in the session object and not removed before the session is closed.
It means that if you use session.get(Customer.class, id) to get a customer with an ID that is already saved before closing the session, it will not cause a SQL SELECT to retrieve this customer from database but simply return the cached customer from the session .
I'm trying to reattach to the session an object from the HTTP session that was originally retrieved from the DB, I do this by calling session.lock(object, LockMode.None) and even though lock does not cascade this works all right for me because it does not push updates to the DB like merge does (the lock is required to open a detail view in a pop-up and the actual saving would occur later on the main window). Now to my surprise I have found that if my entity has a one to many relation any changes on that collection would cause a HibernateException "Reassociated object has dirty collection".
How am I supposed to reattach objects to the session without updating the DB or discarding the changes on the object then?
Here is the situation as code
EntityA t = createAnEntityA();
Session sess = factory.openSession();
sess.beginTransaction();
sess.save(t);
sess.getTransaction().commit();
sess.close();
// t is now saved on the DB but in dettached state
// change a simple property
sess = factory.openSession();
sess.beginTransaction();
t.setPropertyB("B");
sess.lock(t, LockMode.NONE);
// t is attached again, you won't get LazyInitializationException
// by calling its properties, although you have to be careful
// because the reattachment does not cascade to children
sess.getTransaction().commit();
sess.close();
// no updates went to the DB because setPropertyB was called
// when t was still dettached
// now change a collection
EntityC c = createAnEntityC();
t.getCollectionPropertyC().add(c);
sess = factory.openSession();
sess.beginTransaction();
sess.lock(t, LockMode.NONE);
// Exception is thrown :-(
sess.getTransaction().commit();
sess.close();
I'm afraid currently it's not possible without hitting DB.
Apparently, the exception is happening because of lock command.
There's a Jira reporting this behavior.
https://hibernate.atlassian.net/browse/HHH-511
It has 2 patches to fix the problem. You could try those patches.
But if you problem is only Lazy loading of collection, you could consider using Open Session In View pattern. (maybe not the best pattern, but it can work for your case)
I saw to types of update operation:
First:
getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session session) {
session.flush();
session.setCacheMode(CacheMode.IGNORE);
SomeObject ss = (SomeObject) session.get(SomeObject.class, id);
long next = ss.getAndIncrement();
session.update(ss);
session.flush();
return null;
}
});
and secondly
SomeObject ss = loadSomeObject();
long next = ss.getAndIncrement();
getHibernateTemplate.merge(ss);
These two method do the same. I want to know which one is better and safe and why. Thank you.
In the first operation the object ss is attached to the session. where as in the second operation its detached. So if you have an attached objects you can use update. If you have a detached objects then use merge which first attaches the object to the session then will do an update.
EDIT: For your information on attached(persistent) and detached objects :
Hibernate defines and supports the following object states:
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).
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.
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.
Whats an API without any code examples?
SessionFactory sf = ctx.getBean("hibernateSessionFactory",SessionFactory.class);
Session session = sf.openSession();
Transaction t = session.beginTransaction();
try {
Session s2 = sf.openSession();
Organization org = (Organization)s2.get(Organization.class,100624l);//1
org.setOrgName("org");
s2.close();//2
Organization org1 = (Organization)session.get(Organization.class,100624l);//3
org.setOrgName("testOrg");
org1.setOrgName("org");//a
session.merge(org);//4
System.out.println(org == org1);//b
t.commit();
} catch (HibernateException e) {
t.rollback();
throw e;
}finally{
session.close();
}
1st instance is loaded and made persistent.
The instance is detached.
Another instance is loaded
At the point of executing this operation, there are 2 instances of the same object
in the session(org and org1) – org is detached and org1 is persistent
If we do an update() or saveOrUpdate() there, we get the below exception:
org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [com.spring.model.Organization#100624]
a. We do a merge here which:
I. Merges the state of the detached object + persistent object.
II. In case of conflict, the object which is merged wins, like in this case, the value saved will be : testOrg
III. Had we merged on org1, we would have got org.
b. This will always return false, meaning post merge, org was still in DETACHED state
I hope the diff. is clear now.
Summary :
saveOrUpdate() or update() will throw an exception if there are 2 instances of the same object in the session(one detached and one persistent)
merge() will not throw the exception, but will save the object while merging the changes.
Merge Does Following
Merge has intelligence. It has lot of pre-checks before it go actual merge(if required)
if Object is transient, It simply fires INSERT query makes object persistent(attached to session)
if Object is detached, fires select query to check whether data modified or not
if modified, fires UPDATE query otherwise just ignore merge task.
where as session.update
throws exception if object is transient.
if Object is detached, it simply fires UPDATE query irrespective of data changes to object.
session.merge is expensive than update
The basic difference is:
Merge() is not concerned about sessions whether persistent or detached...it will just update without considering the sessions.
In case of update() it will throw an exception like org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session.
It's explained here with a good example:
http://www.java4developer.com/difference-between-update-and-merge-in-hibernate/
Both update() and merge() methods in hibernate are used to convert the object which is in detached state into persistence state. But there is little difference. Let us see which method will be used in what situation.
Let Us Take An Example
SessionFactory factory = cfg.buildSessionFactory();
Session session1 = factory.openSession();
Employee s1 = null;
Object o = session1.get(Employee.class, new Integer(101));
s1 = (Student)o;
session1.close();
s1.setSSN(97);
Session session2 = factory.openSession();
Employee s2 = null;
Object o1 = session2.get(Employee.class, new Integer(101));
s2 = (Student)o1;
Transaction tx=session2.beginTransaction();
session2.merge(s1);
SessionFactory factory = cfg.buildSessionFactory();
Session session1 = factory.openSession();
Employee s1 = null;
Object o = session1.get(Employee.class, new Integer(101));
s1 = (Employee)o;
session1.close();
s1.setMarks(97);
Session session2 = factory.openSession();
Employee s2 = null;
Object o1 = session2.get(Employee.class, new Integer(101));
s2 = (Employee)o1;
Transaction tx=session2.beginTransaction();
session2.merge(s1);
Hope you are clear…, actually update and merge methods will come into picture when ever we loaded the same object again and again into the database, like above.