-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 5 posts ] 
Author Message
 Post subject: org.hibernate.NonUniqueObjectException on proxied objects
PostPosted: Tue Aug 14, 2007 1:44 pm 
Newbie

Joined: Tue Aug 14, 2007 10:14 am
Posts: 3
Hi,

I'm using Hibernate (through Spring) to save a proxied object that contains two other proxied objects. It is not clear that the use of proxies is causing complications; however, they have caused significant issues with hibernate previously.

InflationSwap is an interface to a general busness object container, and amongst other things, it contains a set of InflationSwapPay rows and another set InflationSwapRec rows. These also are interfaces to a more generic ContainerType.

My DAO code is simply:

Code:
   public void save(ContainerObject containerObject) {

      String interfaceName = containerObjectgetClass().getInterfaces()[0].getName();
      getHibernateTemplate().saveOrUpdate(interfaceName, containerObject);
   }



My hibernate mapping file is as follows (I have removed properties and simplified class names for clarity).


Code:
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping auto-import="true">
   <class name="InflationSwap"
      table="security_inflation_swap">
      <id name="securityId" column="security_id" type="int">
         <generator class="assigned" />
      </id>
      </property>
      <set name="leg2_Row_Pay" table="security_inflation_swap_pay" cascade="all">
         <key column="security_id"></key>
         <one-to-many
            class="InflationSwapPayRow"/>
      </set>
      <set name="leg1_Row_Rec" table="security_inflation_swap_rec" cascade="all">
         <key column="security_id"></key>
         
         <one-to-many
            class="InflationSwapRecRow" />

      </set>      
   </class>

   <class name="InflationSwapPayRow" table="security_inflation_swap_pay">
      
      <id name="rowNum" column="row_id" type="int" unsaved-value="0">      
         <generator class="sequence">
            <param name="sequence">SEC_INF_SWAP_SEQ</param>
         </generator>      
      </id>


      <property name="securityId" column="security_id" type="int"></property>

   </class>

   <class name="InflationSwapRecRow" table="security_inflation_swap_rec">

      <id name="rowNum" column="row_id" type="int" unsaved-value="0">      
         <generator class="sequence">
            <param name="sequence">SEC_INF_SWAP_SEQ</param>
         </generator>      
      </id>
      

      <property name="securityId" column="security_id" type="int"></property>
         
   </class>



This gives the following exception

Code:
org.springframework.orm.hibernate3.HibernateSystemException: a different object with the same identifier value was already associated with the session: [com.db.dbiq.calc.security.inflationswap.types.XdbInflationSwapPayRowSave#1]; nested exception is org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [com.db.dbiq.calc.security.inflationswap.types.XdbInflationSwapPayRowSave#1]
   at org.springframework.orm.hibernate3.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:659)
   at org.springframework.orm.hibernate3.HibernateAccessor.convertHibernateAccessException(HibernateAccessor.java:412)
   at org.springframework.orm.hibernate3.HibernateTemplate.execute(HibernateTemplate.java:377)
   at org.springframework.orm.hibernate3.HibernateTemplate.saveOrUpdate(HibernateTemplate.java:697)
   at com.db.dbiq.util.xdbspring.dao.XdbDAO.saveXdbObjectToDatabaseTables(XdbDAO.java:212)
   at com.db.dbiq.xdb.TestInflationSwapBuilder.testPersistInflationSwap(TestInflationSwapBuilder.java:187)
   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
   at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
   at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
   at java.lang.reflect.Method.invoke(Unknown Source)
   at junit.framework.TestCase.runTest(TestCase.java:164)
   at junit.framework.TestCase.runBare(TestCase.java:130)
   at junit.framework.TestResult$1.protect(TestResult.java:110)
   at junit.framework.TestResult.runProtected(TestResult.java:128)
   at junit.framework.TestResult.run(TestResult.java:113)
   at junit.framework.TestCase.run(TestCase.java:120)
   at junit.framework.TestSuite.runTest(TestSuite.java:228)
   at junit.framework.TestSuite.run(TestSuite.java:223)
   at org.junit.internal.runners.OldTestClassRunner.run(OldTestClassRunner.java:35)
   at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:38)
   at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
Caused by: org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [com.db.dbiq.calc.security.inflationswap.types.XdbInflationSwapPayRowSave#1]
   at org.hibernate.engine.StatefulPersistenceContext.checkUniqueness(StatefulPersistenceContext.java:587)
   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$1.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.cascadeCollectionElements(Cascade.java:296)
   at org.hibernate.engine.Cascade.cascadeCollection(Cascade.java:242)
   at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:219)
   at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:169)
   at org.hibernate.engine.Cascade.cascade(Cascade.java:130)
   at org.hibernate.event.def.AbstractFlushingEventListener.cascadeOnFlush(AbstractFlushingEventListener.java:131)
   at org.hibernate.event.def.AbstractFlushingEventListener.prepareEntityFlushes(AbstractFlushingEventListener.java:122)
   at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:65)
   at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:26)
   at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
   at org.springframework.orm.hibernate3.HibernateAccessor.flushIfNecessary(HibernateAccessor.java:390)
   at org.springframework.orm.hibernate3.HibernateTemplate.execute(HibernateTemplate.java:373)
   ... 22 more



I have tried a number of variants, including setting inverse=true and trying to save the child objects directly. The later runs, however fails to update the database. The code for that is roughly:


Code:
      Set leg1 = swap.getLeg1_Row_Rec();
      for (Object row : leg1) {
         InflationSwapRecRow recRow = InflationSwapRecRow) row;
         xdbDao.saveSubObject((SubTableRow)recRow);
      }
      
      Set leg2 = swap.getLeg2_Row_Pay();
      for (Object row : leg2) {
         InflationSwapPayRow recRow = (InflationSwapPayRow) row;
         xdbDao.saveSubObject((SubTableRow)recRow);
      }
      xdbDao.closeConnection();



I have no idea how to solve this problem ATM. I would appreciate any insight anyone may have into this problem.

Thanks,

_________________
---
Jimbot


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 14, 2007 3:04 pm 
Expert
Expert

Joined: Fri Jul 13, 2007 8:18 am
Posts: 370
Location: london
This can happen if you saveOrUpdate a detached instance when the same entity has already been loaded in the current session.
e.g.
Code:
// method to update a detatched instance loaded from a previous session
updateInflationSwap(InflationSwap detatchedInflationSwap) {

  // load the current copy of the detached inflation swap
  InflationSwap currentInflationSwap = (InflationSwap)getHibernateTemplate().load(InflationSwap.class, detatchedInflationSwap.getId());

  // save the changes to the detached inflation swap
  // this causes a NonUniqueObjectException
  getHibernateTemplate().saveOrUpdate(detachedInflationSwap);

}


Could this situation arise in your application? Try doing a merge() instead of saveOrUpdate().


Top
 Profile  
 
 Post subject:
PostPosted: Wed Aug 15, 2007 8:58 am 
Newbie

Joined: Tue Aug 14, 2007 10:14 am
Posts: 3
Hi,

Thanks for your reply. Unfortunately, merge doesn't work. As far as I can make out, merge uses PojoInstantiator to create a copy of the object being merged and copies data across. My classes are cg-lib proxy classes, and the PojoInstantiator can't create them.

My initially feeling was that Hibernate was creating another copy of my object during saveOrUpdate and this is causing a conflict. My reason for believing this was that the objects being saved were newly created objects, and have not been previously retrieved from the database (so the situation you suggest as the cause doesn't arise). However, the instantiate method never gets called. This left me rather confused. Could the cascade be producing the appearance to two objects?

When I removed the cascade, and tried and save each subobject independently, I got no errors; however, the database was not updated. I'm quite new to Hibernate, and I tried setting the show_sql to true in the Spring SessionFactory config, but got no console output. Is there something else I need to do (I'll search the Spring forums is this is too obvious)?

Could this all be caused, in some fashion, by my use of proxied objects. I mentioned that I had problems before: Hibernate, when loading objects, expects the target class to implement the proxy interface, which for various reasons was problematic in my design. I got round this by using a javassist proxy of the target object that I then proxied again using a Spring CG-Lib proxy (perhaps redundant, but there are reasons).

When saving the created objects, I simply used a CG-Lib proxy (no javassist). The main object saved, which led me to believe this would not be a problem. Could it still in some way be a problem for Hibernate?

Thanks,

_________________
---
Jimbot


Top
 Profile  
 
 Post subject:
PostPosted: Wed Aug 15, 2007 11:54 am 
Expert
Expert

Joined: Fri Jul 13, 2007 8:18 am
Posts: 370
Location: london
Quote:
the objects being saved were newly created objects


Are you sure? Looking at the stack trace, hibernate believes your objects are detached and tries to perform an update:
Code:
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)


I've run a test whereby I get spring to create cglib proxied inflation swap and pay/rec rows and they persist without error.


Top
 Profile  
 
 Post subject: Fix to problem
PostPosted: Wed Aug 22, 2007 6:48 am 
Newbie

Joined: Tue Aug 14, 2007 10:14 am
Posts: 3
Hi,

Thanks for your response, that helped identify the problem. For reference, I was saving the parent object, then getting the child objects from it, and saving them. Even though cascade was not set, the initial save attached the child objects: this caused a problem when I came to save them. The solution was to save the child objects first.

Thus:
Code:
      XdbInflationSwapSave swap = (XdbInflationSwapSave) finalInflationswap;
      xdbDao.saveXdbObjectToDatabaseTables(finalInflationswap);
      Set leg1 = swap.getLeg1_Row_Rec();
      for (Object row : leg1) {
         XdbInflationSwapRecRowSave recRow = (XdbInflationSwapRecRowSave) row;
         xdbDao.saveXdbObjectToDatabaseTables((XdbSubTableRow)recRow);
      }
      
      Set leg2 = swap.getLeg2_Row_Pay();
      for (Object row : leg2) {
         XdbInflationSwapPayRowSave recRow = (XdbInflationSwapPayRowSave) row;
         xdbDao.saveXdbObjectToDatabaseTables((XdbSubTableRow)recRow);
      }
      
      xdbDao.closeConnection();
   }


Became:

Code:
      XdbInflationSwapSave swap = (XdbInflationSwapSave) finalInflationswap;
      Set leg1 = swap.getLeg1_Row_Rec();
      for (Object row : leg1) {
         XdbInflationSwapRecRowSave recRow = (XdbInflationSwapRecRowSave) row;
         xdbDao.saveXdbObjectToDatabaseTables((XdbSubTableRow)recRow);
      }
      
      Set leg2 = swap.getLeg2_Row_Pay();
      for (Object row : leg2) {
         XdbInflationSwapPayRowSave recRow = (XdbInflationSwapPayRowSave) row;
         xdbDao.saveXdbObjectToDatabaseTables((XdbSubTableRow)recRow);
      }
      xdbDao.saveXdbObjectToDatabaseTables(finalInflationswap);
      xdbDao.closeConnection();
   }


Perhaps this is a well documented feature of Hibernate, but the solution is hopefuuly useful to other newbies.

_________________
---
Jimbot


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 5 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.