-->
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.  [ 6 posts ] 
Author Message
 Post subject: Merging the the new object second time doesn't work
PostPosted: Mon Jun 16, 2008 11:00 am 
Newbie

Joined: Wed May 07, 2008 11:04 pm
Posts: 1
Need help with Hibernate? Read this first:
http://www.hibernate.org/ForumMailingli ... AskForHelp

Hello,

I'm trying to save a new object (which doesn't exist in database already) in the database using session.merge(). Though it works fine when session.merge() is called only once, it throws exception when session.merge(obj) is called second time. Note that this is not an issue if the object is loaded from the database (i.e., we can call session.merge(obj) multiple times if the object is loaded from database)

Note that as per requirements we want to use the same session through out application life time (so, closing and opening the session is ruled out) and we don't want to use (session.saveOrUpdate(obj)).

Please let me know if I'm not clear or I need to provide any other information.

thanks
Ranadheer

Hibernate version: 3.2.6

Mapping documents:

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

<session-factory>

<!-- Database connection settings -->
<property name="connection.driver_class">com.sybase.jdbc3.jdbc.SybDriver</property>
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">1</property>

<!-- SQL dialect -->
<property name="dialect">org.hibernate.dialect.SybaseDialect</property>

<!-- Enable Hibernate's automatic session context management -->
<property name="current_session_context_class">thread</property>

<!-- Disable the second-level cache -->
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>

<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>

<mapping class="hello.Person"/>
<mapping class="hello.PersonInfo"/>

</session-factory>
</hibernate-configuration>


Code between sessionFactory.openSession() and session.close():



Full stack trace of any exception that occurs:
Exception in thread "main" org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [hello.PersonInfo#ppp]
at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:168)
at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:121)
at org.hibernate.event.def.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:186)
at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:123)
at org.hibernate.impl.SessionImpl.fireMerge(SessionImpl.java:687)
at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:669)
at org.hibernate.engine.CascadingAction$6.cascade(CascadingAction.java:245)
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.event.def.DefaultMergeEventListener.cascadeOnMerge(DefaultMergeEventListener.java:407)
at org.hibernate.event.def.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:266)
at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:120)
at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:53)
at org.hibernate.impl.SessionImpl.fireMerge(SessionImpl.java:677)
at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:661)
at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:665)
at hello.HelloPerson.save(HelloPerson.java:31)
at hello.HelloPerson.main(HelloPerson.java:52)


Name and version of the database you are using: Sybase & Adavptive Server Enterprice 15.0

The generated SQL (show_sql=true):
Hibernate: select this_.name as name0_1_, personinfo2_.name as name1_0_ from person this_ left outer join person_info personinfo2_ on this_.name=personinfo2_.name where this_.name=?
Hibernate: select person0_.name as name0_1_, personinfo1_.name as name1_0_ from person person0_ left outer join person_info personinfo1_ on person0_.name=personinfo1_.name where person0_.name=?
Hibernate: insert into person (name) values (?)
Hibernate: insert into person_info (name) values (?)


Debug level Hibernate log excerpt:
Hibernate: select this_.name as name0_1_, personinfo2_.name as name1_0_ from person this_ left outer join person_info personinfo2_ on this_.name=personinfo2_.name where this_.name=?
Hibernate: select person0_.name as name0_1_, personinfo1_.name as name1_0_ from person person0_ left outer join person_info personinfo1_ on person0_.name=personinfo1_.name where person0_.name=?
Hibernate: insert into person (name) values (?)
Hibernate: insert into person_info (name) values (?)
Exception in thread "main" org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [hello.PersonInfo#ppp]
at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:168)
at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:121)
at org.hibernate.event.def.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:186)
at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:123)
at org.hibernate.impl.SessionImpl.fireMerge(SessionImpl.java:687)
at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:669)
at org.hibernate.engine.CascadingAction$6.cascade(CascadingAction.java:245)
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.event.def.DefaultMergeEventListener.cascadeOnMerge(DefaultMergeEventListener.java:407)
at org.hibernate.event.def.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:266)
at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:120)
at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:53)
at org.hibernate.impl.SessionImpl.fireMerge(SessionImpl.java:677)
at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:661)
at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:665)
at hello.HelloPerson.save(HelloPerson.java:31)
at hello.HelloPerson.main(HelloPerson.java:52)



Problems with Session and transaction handling?

Read this: http://hibernate.org/42.html

Code:

/** HibernateUtil.java **/
package util;

import org.hibernate.*;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.criterion.Criterion;

public class HibernateUtil {
   
    private static final SessionFactory ourSessionFactory;
    private static Session ourSession;
   
    static {
        try {
            ourSessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
            ourSession = HibernateUtil.getSessionFactory().openSession();
        } catch (Throwable ex) {
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static SessionFactory getSessionFactory() {
        return ourSessionFactory;
    }
   
    public static Session getSession() {
       if (!ourSession.isOpen()) {
            ourSession = ourSessionFactory.openSession();
       }
        return ourSession;
    }
   
    public static<T> T findUniqueByCriteria(Class<?> persistentClass,
          Criterion... criterion)
    {
       Criteria criteria =
          getSession().createCriteria(persistentClass);

       for (Criterion c : criterion) {
          criteria.add(c);
       }

       return (T) criteria.uniqueResult();
    }
}


Code:

/** Person.java **/
package hello;

import javax.persistence.*;
import javax.persistence.Table;

@Entity
@Table(name = "person")
public class Person
{
    private String myName;
    private PersonInfo myPersonInfo;

    public Person() {}

    public Person(String name)
    {
        myName = name;
    }

    @Id
    @Column(name = "name")
    public String getName()
    {
        return myName;
    }

    public void setName(String name)
    {
        myName = name;
    }
   
    @OneToOne(cascade = CascadeType.ALL,  optional = true)
    @PrimaryKeyJoinColumn
    public PersonInfo getPersonInfo()
    {
        return myPersonInfo;
    }

    public void setPersonInfo(PersonInfo personInfo)
    {
        myPersonInfo = personInfo;
    }
}


Code:
/** PersonInfo **/
package hello;

import javax.persistence.*;
import org.hibernate.annotations.Parameter;

@Entity
@Table(name="person_info")
@Inheritance(strategy=InheritanceType.JOINED)
public class PersonInfo
{
    private String myPersonId;
    private Person myPerson;

    @Id @GeneratedValue(generator = "myForeignGenerator")
    @org.hibernate.annotations.GenericGenerator(
        name = "myForeignGenerator",
        strategy = "foreign",
        parameters = @Parameter(name = "property", value = "person"))
    @Column (name = "name")
    public String getPersonId()
    {
        return myPersonId;
    }

    public void setPersonId(String personId)
    {
        this.myPersonId = personId;
    }

    @OneToOne(cascade = CascadeType.ALL, optional = false)
    @PrimaryKeyJoinColumn
    public Person getPerson()
    {
        return myPerson;
    }

    public void setPerson(Person person)
    {
        myPerson = person;
    }
}


Code:

package hello;

import util.HibernateUtil;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Expression;

public class HelloPerson
{
    private static Person save(Person person)
    {
        Transaction tx = null;
        Session sess = HibernateUtil.getSession();
        tx = sess.beginTransaction();
        sess.merge(person);
        tx.commit();
        return person;
    }
   
    public static void main(String[] args)
    {
        String name = "ravi";
      
        Criterion criterion = Expression.eq("name", name);
        Person person =
            HibernateUtil.findUniqueByCriteria(Person.class, criterion);
       
        if (person == null) {
           person = new Person(name);
           PersonInfo personInfo = new PersonInfo();
           person.setPersonInfo(personInfo);
           personInfo.setPerson(person);
        }

        save(person);
        save(person);
    }
}


Last edited by Ranadheer on Mon Sep 08, 2008 10:02 am, edited 1 time in total.

Top
 Profile  
 
 Post subject: Re: Merging the the new object second time doesn't work
PostPosted: Wed Jun 18, 2008 7:49 am 
Newbie

Joined: Wed Jun 18, 2008 7:30 am
Posts: 3
Location: Delhi
Hi,

The reason you getting this exception, because an object with the same identifier is already present in the session.
Before merging the same person object, you need to first remove the older version of the object from the session.

Simply remove the object from the hibernate session, once it is saved into the database.

Add the following two lines below in red into your code and it will work fine.

private static Person save(Person person)
{
Transaction tx = null;
Session sess = HibernateUtil.getSession();
tx = sess.beginTransaction();
sess.merge(person);
//execute the query to save the object
sess.flush();
//remove the saved person from the session
sess.evict(person);
tx.commit();
return person;
}


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jun 18, 2008 10:02 am 
Newbie

Joined: Wed May 14, 2008 8:36 am
Posts: 6
Hi,

i'm also facing same problem and above solution seems like a workaround to me coz
- evict will remove the complete object tree from session, so when writing object tree second time, it is taking more queries, time.
- also as merge javadoc suggests, merge should automatically update the difference in database if the object is a persistent instance , why is it trying to add a new row even though another row with same id exists?


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jun 19, 2008 9:15 am 
Expert
Expert

Joined: Tue May 13, 2008 3:42 pm
Posts: 919
Location: Toronto & Ajax Ontario www.hibernatemadeeasy.com
Sheetu is correct:

Quote:
Simply remove the object from the hibernate session, once it is saved into the database.


Of course, this means you have two isntances of the same entity within the same transaction. There might be an opportunity to refactor the design to leverage that existing entity, rather than creating a new one.

By the way, sometimes if you have multiple sessions accessing records, and you start swapping entities between sessions, you'll inadvertently get this problem. Are you using openSession or getSession? The distinction between the two Hiberante3 Session objects might be a mitigating factor.

_________________
Cameron McKenzie - Author of "Hibernate Made Easy" and "What is WebSphere?"
http://www.TheBookOnHibernate.com Check out my 'easy to follow' Hibernate & JPA Tutorials


Top
 Profile  
 
 Post subject:
PostPosted: Sat Jun 21, 2008 9:44 am 
Newbie

Joined: Wed May 14, 2008 8:36 am
Posts: 6
i.


Top
 Profile  
 
 Post subject: How to use session.Merge(object) ?
PostPosted: Sun Oct 19, 2008 7:51 am 
Newbie

Joined: Thu Aug 14, 2008 10:08 am
Posts: 2
Hi,

I am newbie to hibernate. I am facing the same problem, here I tried to use Person object returned by merge and it worked fine.

Here are the changes to the code, there is no need to evict the object from the session.
---------------
private static Person save(Person person)
{
Transaction tx = null;
Session sess = HibernateUtil.getSession();
tx = sess.beginTransaction();
person = (Person) sess.merge(person);
tx.commit();
return person;
}

public static void main(String[] args)
{
String name = "ravi";

Criterion criterion = Expression.eq("name", name);
Person person =
HibernateUtil.findUniqueByCriteria(Person.class, criterion);

if (person == null) {
person = new Person(name);
PersonInfo personInfo = new PersonInfo();
person.setPersonInfo(personInfo);
personInfo.setPerson(person);
}

person = save(person);
save(person);
}
--------

Cameron,

Can you please confirm whether this approach is correct or not ? I think in case of merge it would be safe to use the object returned by merge, correct ?

Thanks,
~Kesam


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