Hi all,
I've been looking for solutions but cannot find any and it's making me crazy... After having debugged and debugged my app, I have come to the point that the problem can be resumed in this simple test case :
I have a simple class
Activity which is mapped to a database table
REP_ACTIVITIES (see mapping documents below). I try to load it twice by hibernate and compare the 2 generated instances :
Code:
UID i = new UID(4);
IActivity firstActivity = (IActivity)IdentifiableDAO.getInstance().load(Activity.class, i);
IActivity secondActivity = (IActivity)IdentifiableDAO.getInstance().load(Activity.class, i);
if(firstActivity!=secondActivity) {
System.out.println("This is weird");
}
Here is the implementation of my
IdentifableDAO.load(Class,UID) method :
Code:
public IdentifiedObject load(Class clazz, UID id) {
log.debug("Loading class '" + clazz.getName() + "' for ID "+ id);
Session session = hUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
//Trying to retrieve in-session object
IdentifiedObject object = (IdentifiedObject)session.get(clazz, id.rawId());
session.getTransaction().commit();
return object;
}
And here's my problem :
The 2 returned instances from the 2 consecutive calls to hibernate are different ! I was first calling Session.load(Class,long) to load my objects and first thought that converting it to a Session.get(Class,long) call will retrieve the instance of Activity
in Session. But get has exactly the same behaviour since the object always exists in database...
After this kind of "double load", if i try some saveOrUpdate call on the second instance i will get a NonUniqueObjectException.
I am looking for a way to retrieve the same object instance when passing the same long id to a load operation (or get, or anything that can first check in the Session "cache"). I thought hibernate would do it! Maybe i have done something wrong but i cannot see what or find anything on forums.
It seems like hibernate knows that it has already loaded the same object (as it raises a NonUniqueObjectException on save) but still instantiate a new object for an id of an alreay loaded object.
I've got a workaround in my IdentifableDAO.load method which is implementing a cache of loaded hibernate objects, but it seems to me weird! I would need something in hibernate that would do this for me :
Code:
public IdentifiedObject load(Class clazz, UID id) {
log.debug("Loading class '" + clazz.getName() + "' for ID "+ id);
Code:
// Manually implementing a Session object cache (which exists in hibernate !)
IdentifiedObject object = hibernateObjects.get(new MultiKey(clazz,id));
// If not in my hibernate cache then
if(object==null) {
Code:
Session session = hUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
//Trying to retrieve in-session object
object = (IdentifiedObject)session.get(clazz, id.rawId());
session.getTransaction().commit();
Code:
// Registering loaded object in cache
hibernateObjects.put(new MultiKey(clazz,id), object);
}
Code:
return object;
}
I'm sure hibernate provides a way to do this, i cannot imagine that 2 load operation on a same id cannot return the same instance. Could you please help me ?
I know you can ;-)
Thx.
Hibernate version: 3.2
Mapping documents:Mapping file :
Code:
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.nextep.datadesigner.vcs.impl.Activity" table="REP_ACTIVITIES" lazy="false">
<id name="id" column="ACTIVITY_ID">
<generator class="increment"/>
</id>
<property name="name" column="ACTIVITY_NAME"/>
<property name="description" column="DESCRIPTION"/>
</class>
</hibernate-mapping>
Hibernate configuration :
Code between sessionFactory.openSession() and session.close():Not much code :
Code:
session.beginTransaction();
//Trying to retrieve in-session object
IdentifiedObject object = (IdentifiedObject)session.get(clazz, id.rawId());
session.getTransaction().commit();
Full stack trace of any exception that occurs:If for some reason i try to persist in a same Session an object grappe which references this 2 instances (my real app problem), i will have a NonUniqueObjectException :
Code:
Exception in thread "main" org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [com.nextep.datadesigner.vcs.impl.Activity#4]
at org.hibernate.engine.StatefulPersistenceContext.checkUniqueness(StatefulPersistenceContext.java:590)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:284)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventListener.java:223)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:89)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:70)
at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:507)
at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:499)
at org.hibernate.engine.CascadingAction$5.cascade(CascadingAction.java:218)
at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:268)
at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:216)
at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:169)
at org.hibernate.engine.Cascade.cascade(Cascade.java:130)
at org.hibernate.engine.Cascade.cascade(Cascade.java:97)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.cascadeOnUpdate(DefaultSaveOrUpdateEventListener.java:357)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:329)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventListener.java:223)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:89)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:70)
at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:507)
at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:499)
at org.hibernate.engine.CascadingAction$5.cascade(CascadingAction.java:218)
at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:268)
at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:216)
at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:169)
at org.hibernate.engine.Cascade.cascade(Cascade.java:130)
at org.hibernate.engine.Cascade.cascade(Cascade.java:97)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.cascadeOnUpdate(DefaultSaveOrUpdateEventListener.java:357)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:329)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventListener.java:223)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:89)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:70)
at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:507)
at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:499)
at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:495)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.hibernate.context.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:301)
at $Proxy0.saveOrUpdate(Unknown Source)
at com.nextep.datadesigner.dao.IdentifiableDAO.save(IdentifiableDAO.java:87)
Name and version of the database you are using:
Oracle 10.2
The generated SQL (show_sql=true):
Hibernate: select activity0_.ACTIVITY_ID as ACTIVITY1_2_0_, activity0_.ACTIVITY_NAME as ACTIVITY2_2_0_, activity0_.DESCRIPTION as DESCRIPT3_2_0_ from REP_ACTIVITIES activity0_ where activity0_.ACTIVITY_ID=?
Hibernate: select activity0_.ACTIVITY_ID as ACTIVITY1_2_0_, activity0_.ACTIVITY_NAME as ACTIVITY2_2_0_, activity0_.DESCRIPTION as DESCRIPT3_2_0_ from REP_ACTIVITIES activity0_ where activity0_.ACTIVITY_ID=?
Debug level Hibernate log excerpt:
Problems with Session and transaction handling?
Read this:
http://hibernate.org/42.html