-->
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.  [ 13 posts ] 
Author Message
 Post subject: session.merge & saveOrUpdate->NonUniqueObjectExceptio
PostPosted: Sat Mar 25, 2006 1:54 pm 
Expert
Expert

Joined: Mon Jan 09, 2006 5:01 pm
Posts: 311
Location: Sacramento, CA
Tested with Hibernate 3.1.2, java 1.5.0_05

Here is a test case - basically testing to see if session.MERGE () works correctly, as stated in the docs. The test shows that it doesn't!!! From the docs, the session.merge(object) function should do this:

Quote:
The state of a transient or detached instance may also be made persistent as a new persistent instance by calling merge().


The code works as shown right now

here is the problem:
if I comment out sess.merge() I get org.hibernate.NonUniqueObjectException
if enable sess.merge() and I comment out sess.saveOrUpdate()
It doesn't save!!!


So here is the 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="test_merge.TestObj" table="TestObj">
<id name="to_id"
    column="to_id"
    unsaved-value="null">
            <generator class="hilo">
                <param name="table">to_seq</param>
                <param name="column">next</param>
            </generator>
           
</id>
<property name="name" type="string"/>
</class>
</hibernate-mapping>


Java code for TestObj:

Code:
package test_merge;


/**
* @author jt_1000
  */
public class TestObj {
    Long to_id=null;
    String name=null;

   public String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }
 
   public Long getTo_id() {
      return to_id;
   }
   public void setTo_id(Long to_id) {
      this.to_id = to_id;
   }
}


The part about thread is irrelevant, it does the same thing when working in the current thread.

scenario 1:

(session->load->copy object ->close session; new session -> merge(copy)->saveOrUpdate [throws NonUniqueObjectException])

scenario 2:
(session->load->copy object ->close session; new session -> merge(copy)->commit [nothing is saved])

scenario 3:
(session->load->copy object ->close session; new session -> saveOrUpdate (copy)->commit [saved correctly, only because unsaved-value="null", on ID])

If I put hasCode and/or equals on TestObj still the same results from each of the above scenarios.

I have other reasons to want to get the merge working, since I noticed that collections of children from a parent will behave differently when modifing the collection item (add/delete) and modifying an item, for each situation - if in the session already it will behave correctly with <set inverse="true"... if not in the session that behavior (add/delete items and modify item) doesn't work properly. It only works properly by doing the saveOrUpdate on the parent and the <set ...inverse="false"> (collection managed by parent.) - It works, but with one caveate - it doesn't handle not-null on the FK in the child very well, and won't do delete-orphan.

Here is the Java code that excercises the Java/Mapping:
Code:
package test_merge;

import com.rhinosystemsinc.hibernate.Util.HibernateUtil_V3;
import java.util.*;
import org.hibernate.*;

public class Main {
   public static void copyObject(TestObj to,TestObj tocopy)
   {
      tocopy.setName(new String(to.getName()));
      tocopy.setTo_id(new Long(to.getTo_id().longValue()));
      
   }
   public static void main(String args[]) {
      final SessionFactory sf = HibernateUtil_V3.getSessionFactory(
            "hibernate.properties", new Class[] { TestObj.class });
      final TestObj toCopy=new TestObj();
      //simulate some other request by a thread that loads
      //the object that I am trying to save from the main thread
      //simulated by the copyObject function.
      Thread t=new Thread(){
         public void run()
         {
            Session sess = sf.openSession();
            System.out.println("first session id=" + sess.hashCode());
            TestObj to=null;
            to = (TestObj)sess.load(TestObj.class,new Long(196608));
            to.setName("object 1");
            copyObject(to,toCopy);
            sess.close();   
         }
      };
      t.start();
      System.out.println("started thread");
      try {
         System.out.println("wait on thread");
         t.join(); //wait for thread to finish.
      } catch (InterruptedException e) {
         System.out.println("E=" + e.getMessage());
         e.printStackTrace();
      }
      System.out.println("continue");
      
      /**
The code works as shown right now

here is the problem:
if I comment out sess.merge() I get   org.hibernate.NonUniqueObjectException
if enable sess.merge() and I comment out sess.saveOrUpdate()
It doesn't save.
       */
      Session sess = sf.openSession();
      sess=sf.openSession();
      System.out.println("new session id=" + sess.hashCode());
      //sess.merge(toCopy);
      Transaction tx=sess.beginTransaction();
      //change the detached object.
      toCopy.setName("change: " + new Date());
      sess.saveOrUpdate(toCopy);
      tx.commit();

      Query q = sess.createQuery("from TestObj");
      java.util.List result = q.list();
      //sess.close();
      java.util.Iterator itr = null;
      TestObj l = null;
      if (result != null) {
         itr = result.iterator();
         while (itr != null && itr.hasNext()) {
            l = (TestObj) itr.next();
            System.out.println("id=" + l.getTo_id());
            System.out.println("name=" + l.getName());
         }
      }
   }   
}


Top
 Profile  
 
 Post subject:
PostPosted: Tue Mar 28, 2006 12:19 pm 
Expert
Expert

Joined: Mon Jan 09, 2006 5:01 pm
Posts: 311
Location: Sacramento, CA
guys - any ideas on this? anyone test it themselves?


Top
 Profile  
 
 Post subject:
PostPosted: Thu Apr 13, 2006 4:39 pm 
Regular
Regular

Joined: Sat Jan 07, 2006 8:30 pm
Posts: 68
I don't think it works... I haven't run your test case but I ran a test in my web application. Mine actually fails due the fact that one of my columns had a not-null="true"

<property name="paxStatus" type="string">
<column name="pax_status" length="9" not-null="true" />
</property>

Check the results here
http://forum.hibernate.org/viewtopic.ph ... highlight=

Anyhow, I think that I need to add a version field to the mapping on those object that need to support the merge feature...

I will try tonight - I hope won't fail since I already spent some time on this matter and I don't want to manually merge my detached instance if hibernate can do the job!

Q


Top
 Profile  
 
 Post subject: Answer
PostPosted: Fri Apr 14, 2006 3:23 pm 
Newbie

Joined: Fri Apr 14, 2006 3:18 pm
Posts: 1
The problem with your example is that you need to do your persistence operations on the object returned from merge.

Something like this should work
...

toCopy = (TestObj) sess.merge(toCopy);
Transaction tx=sess.beginTransaction();
//change the detached object.
toCopy.setName("change: " + new Date());
sess.saveOrUpdate(toCopy);

Regards,
Eric Meyer, VP, Quoin, Inc.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Apr 14, 2006 5:36 pm 
Regular
Regular

Joined: Wed Jul 07, 2004 2:00 pm
Posts: 64
I also believe that the merge should be moved to after the beginTransaction.


Top
 Profile  
 
 Post subject: Re: Answer
PostPosted: Sun Apr 16, 2006 11:46 pm 
Regular
Regular

Joined: Sat Jan 07, 2006 8:30 pm
Posts: 68
I will read the documentation again but I don't think you're right...

I thought merge() is "merging" a detached object into the persisted object.
My problem is that the detached object has one field that is marched as not-null and it is null.

Seems that I have toload and then saveOrUpdate...

Thanks for looking!


Top
 Profile  
 
 Post subject:
PostPosted: Mon Apr 17, 2006 2:35 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 7:19 pm
Posts: 2364
Location: Brisbane, Australia
Merge does save an unsaved object - I tried a simple sample and it works fine.

Merge is similar to update - all the fields are stored (unless dynamic update is enabled) so if the object to merge has a null value when it should not because of not-null constraint then you would expect it to complain. The word merge is refering to the way the original transient object is not included in the session while a new (merged) domain object is.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Apr 17, 2006 4:38 pm 
Regular
Regular

Joined: Sat Jan 07, 2006 8:30 pm
Posts: 68
Thanks David!

So basically merge() saveOrUpdate() would behave similarely... (there are small difference but the end result will be the same updating a row with new dataset)

Talking about dynamic update - I see that is by default set to false.
Is that because the fact the query is dynamically build on the fly and the cost for that is higher than the gain of creating/executing a smaller update query ?

Q


Top
 Profile  
 
 Post subject:
PostPosted: Mon Apr 17, 2006 8:14 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 7:19 pm
Posts: 2364
Location: Brisbane, Australia
Yes - there are a few minor differences but merge and saveOrUpdate(Copy) behave similarely.

Normally you would not want to enable dynamic update for the reasons you have stated. There are cases, such as huge number of columns in a table, that dynamic update could be better but its a case by case senario.


Top
 Profile  
 
 Post subject:
PostPosted: Sat Apr 22, 2006 11:51 am 
Expert
Expert

Joined: Mon Jan 09, 2006 5:01 pm
Posts: 311
Location: Sacramento, CA
thanks for the help. Here is the code that works.

Code:
      
toMergeCopy=(TestObj)sess.merge(toCopy);
Transaction tx=sess.beginTransaction();
//change the detached object.
toCopy.setName("change: " + new Date()); //has no affect on merged object
//sess.saveOrUpdate(toMergeCopy);
tx.commit();


It works in or out of a transaction block.

David,

I'd like to point out that most of the examples in the test cases, notably MergeTest.java don't show usage of the merged object being the one returned from the session.merge call.

Since we draw alot of our knowledge by example from the test-cases, and not to mention that a "merged" object test should be done...shouldn't the tests be properly done.. since sess.merge(obj) (w/no return) should have no affect, yet it is used throughout the test cases.

eg, see the 3.1 distrubution:
test\org\hibernate\test\ops\MergeTest.java (testMergeTreeWithGeneratedId)

Why should the testMergeTreeWithGeneratedId method work?


Top
 Profile  
 
 Post subject:
PostPosted: Mon Apr 24, 2006 4:17 pm 
Newbie

Joined: Mon Apr 24, 2006 2:01 pm
Posts: 13
Hello there,

I thought this hread will help me solve my issue but did not. I'm getting the following error on the merge:


15:28:36 ERROR an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session)
org.hibernate.AssertionFailure: entity was not detached
at org.hibernate.event.def.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:201)
at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:102)
at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:544)
at org.hibernate.engine.Cascades$6.cascade(Cascades.java:176)
at org.hibernate.engine.Cascades.cascadeAssociation(Cascades.java:771)
at org.hibernate.engine.Cascades.cascade(Cascades.java:720)
at org.hibernate.engine.Cascades.cascadeCollection(Cascades.java:895)
at org.hibernate.engine.Cascades.cascadeAssociation(Cascades.java:792)
at org.hibernate.engine.Cascades.cascade(Cascades.java:720)
at org.hibernate.engine.Cascades.cascade(Cascades.java:847)
at org.hibernate.event.def.DefaultMergeEventListener.cascadeOnMerge(DefaultMergeEventListener.java:264)
at org.hibernate.event.def.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:223)
at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:102)
at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:54)
at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:535)
at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:539)
at com.intercall.applications.owner.OwnerLibraryAPIBean.saveParticipants(OwnerLibraryAPIBean.java:174)
at com.intercall.applications.owner.OwnerLibraryAPI_46j5ow_EOImpl.saveParticipants(OwnerLibraryAPI_46j5ow_EOImpl.java:46)
at com.intercall.applications.owner.OwnerLibraryAPI_46j5ow_EOImpl_WLSkel.invoke(Unknown Source)
at weblogic.rmi.internal.ServerRequest.sendReceive(ServerRequest.java:166)
at weblogic.rmi.cluster.ReplicaAwareRemoteRef.invoke(ReplicaAwareRemoteRef.java:290)
at weblogic.rmi.cluster.ReplicaAwareRemoteRef.invoke(ReplicaAwareRemoteRef.java:247)




I have a graph of object that is coming from the client and the server needs to merge with the database. The incoming objects are not yet loaded in the session.

The top level object is already exists in the DB but child is not (new object). I have cascade="all,delete-orphan" specified in the parent object mapping.

Based on this thread discussions, When call session.merge (topLevelObject) will the child saved to the database ?


But getting the above error ...... Any help would be greately appreciated.


Thanks


Top
 Profile  
 
 Post subject:
PostPosted: Fri Mar 06, 2009 7:56 am 
Regular
Regular

Joined: Tue Feb 17, 2009 5:13 am
Posts: 59
Quote:
The state of a transient or detached instance may also be made persistent as a new persistent instance by calling merge().


You're right, but if read the merge API documentation http://www.hibernate.org/hib_docs/v3/api/org/hibernate/Session.html#merge(java.lang.Object), you'll see this:
Quote:
...The given instance does not become associated with the session. ...


So, if the returned copy is not associated with the session, it will not be automatically saved by Hibernate. So you must call save (saeOrUpdate) method after merge.[/url]


Top
 Profile  
 
 Post subject: Re: session.merge & saveOrUpdate->NonUniqueObjectExceptio
PostPosted: Tue Aug 03, 2010 2:29 am 
Newbie

Joined: Tue Aug 03, 2010 2:25 am
Posts: 1
I struggled for eternity with this exception as well. In my case, I was trying calling save on an object that had a collection with one of the values being a BigDecimal. I was trying to persist it in Sybase. I would get this error when attempting to save or merge the object( along with the collection). What was happening was that the although the id on the collection was mapped as @GeneratedValue(strategy = GenerationType.AUTO), the values being generated by the Sybase driver for instance [3] and for instance[4] of the collection were the same. Even though the value was different [0.000 and 2.04343].

My Eureka moment was when i checked the table creation script, and the table column had a scale of 4 (percent decimal(18,4) NOT NULL,) whereas the BigDecimal default is a scale of 15. The Sybase driver could not handle it appropriately. Fixing the scale to match the table value fixed the issue.
It was related to viewtopic.php?f=1&t=980086&view=previous


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 13 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.