views:

1016

answers:

2

I want to compare the current value of an in-memory Hibernate entity with the value in the database:

HibernateSession sess = HibernateSessionFactory.getSession();
MyEntity newEntity = (MyEntity)sess.load(MyEntity.class, id);
newEntity.setProperty("new value");
MyEntity oldEntity = (MyEntity)sess.load(MyEntity.class, id);
// CODEBLOCK#1 evaluate differences between newEntity and oldEntity
sess.update(newEntity);

In CODEBLOCK#1 I get that newEntity.getProperty()="new value" AND oldEntity.getProperty()="new value" (while I expected oldEntity.getProperty()="old value", of course). In fact the two objects are exactly the same in memory.

I messed around with HibernateSessionFactory.getSession().evict(newEntity) and attempted to set oldEntity=null to get rid of it (I need it only for the comparison):

HibernateSession sess = HibernateSessionFactory.getSession();
MyEntity newEntity = (MyEntity)sess.load(MyEntity.class, id);
newEntity.setProperty("new value");
HibernateSessionFactory.getSession().evict(newEntity);
MyEntity oldEntity = (MyEntity)sess.load(MyEntity.class, id);
// CODEBLOCK#1 evaluate differences between newEntity and oldEntity
oldEntity = null;
sess.update(newEntity);

and now the two entities are distinct, but of course I get the dreaded org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session.

Any idea?

EDIT: I tried the double session strategy; I modified my HibernateSessionFactory to implement a map of session and then...

Session session1 = HibernateSessionFactory.getSession(SessionKeys.DEFAULT);
Session session2 = HibernateSessionFactory.getSession(SessionKeys.ALTERNATE);
Entity newEntity = (Entity)entity;
newEntity.setNote("edited note");
Entity oldEntity = (Entity)session1.load(Entity.class, id);

System.out.println("NEW:" + newEntity.getNote());
System.out.println("OLD: " + oldEntity.getNote()); // HANGS HERE!!!

HibernateSessionFactory.closeSession(SessionKeys.ALTERNATE);

My unit test hangs while attempting to print the oldEntity note... :-(

A: 

What about using session.isDirty()? The JavaDoc says that method will answer the question "Would any SQL be executed if we flushed this session?" Of course, that would only work if you had a new clean Session to start with. Another option - just use two different Sessions.

Brian Deterling
+2  A: 

Two easy options spring to mind:

  1. Evict oldEntity before saving newEntity
  2. Use session.merge() on oldEntity to replace the version in the session cache (newEntity) with the original (oldEntity)

EDIT: to elaborate a little, the problem here is that Hibernate keeps a persistence context, which is the objects being monitored as part of each session. You can't do update() on a detached object (one not in the context) while there's an attached object in the context. This should work:

HibernateSession sess = ...;
MyEntity oldEntity = (MyEntity) sess.load(...);
sess.evict(oldEntity); // old is now not in the session's persistence context
MyEntity newEntity = (MyEntity) sess.load(...); // new is the only one in the context now
newEntity.setProperty("new value");
// Evaluate differences
sess.update(newEntity); // saving the one that's in the context anyway = fine

and so should this:

HibernateSession sess = ...;
MyEntity newEntity = (MyEntity) sess.load(...);
newEntity.setProperty("new value");
sess.evict(newEntity); // otherwise load() will return the same object again from the context
MyEntity oldEntity = (MyEntity) sess.load(...); // fresh copy into the context
sess.merge(newEntity); // replaces old in the context with this one
Cowan
Could you please elaborate? I tried both, but neither worked... maybe I'm doing it wrong
Manrico Corazzi
I did it with strategy #2, tnx
Manrico Corazzi