-->
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.  [ 23 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: NonUniqueObjectException
PostPosted: Thu Jul 01, 2004 2:07 pm 
Beginner
Beginner

Joined: Thu Oct 09, 2003 11:41 am
Posts: 39
Location: Paris, France
The following code throws a NonUniqueObjectException exception

MyClass n = (MyClass)session.load(MyClass.class, new Long(id));
activeUser.setSomeProperty(n);
session.update(activeUser);

The mapping is as follows:

Class User
<many-to-one name="mappingname" class="MyClass" column="some_column" cascade="all"/>

and the other mapping has this line:

Class MyClass
<many-to-one name="myname" class="User" column="id"/>

This is the exception:

net.sf.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: 1, of class: User

Any ideas?


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jul 02, 2004 1:29 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 3:00 pm
Posts: 1816
Location: Austin, TX
Show the <id/> element for your User class, including its <generator/> element.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jul 02, 2004 2:31 am 
Beginner
Beginner

Joined: Thu Oct 09, 2003 11:41 am
Posts: 39
Location: Paris, France
Thanks for your answer.

<id name="id" column="user_id" type="java.lang.Long">
<generator class="increment"/>
</id>

increment is due to the fact that we could not get MySQL to work with HiLoGenerator via MBean in JBoss. It however seems to work with increment.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jul 02, 2004 2:32 am 
Beginner
Beginner

Joined: Thu Oct 09, 2003 11:41 am
Posts: 39
Location: Paris, France
user_id should read id...


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jul 02, 2004 2:57 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 3:00 pm
Posts: 1816
Location: Austin, TX
hilo will not work with JTA enlisted connections.

Did not notice in your initial post (getting way to late here), what is a bi-directional many-to-one? Thats an impossibility. A many-to-one implies that one side must be many-valued. It looks like you really want a one-to-one relationship.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jul 02, 2004 3:06 am 
Beginner
Beginner

Joined: Thu Oct 09, 2003 11:41 am
Posts: 39
Location: Paris, France
Ok, I've got a user who has a property. This property is represented by an object. Once the user clicks a link, there is another object that is created and assigned to the user.

In reality the user flips pages, so the user record in the database always points to the current record. If the user closes the browser, the system will know next time s/he logs in what the last page was. Besides, the user must perform some business logic on the pages, thus the page property indicates the page the system is working with.

This should be a many-to-one relationship, shouldn't it? Many pages->one user.

When the record is updated however (as simple as this), there is this exception. I tried to evict the instance (not exactly clear how eviction works) but that did not work.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jul 02, 2004 3:17 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 3:00 pm
Posts: 1816
Location: Austin, TX
pages and users? in your initial post your talking about User and MyClass.

how about you post the mapping files that are causing you problems along with the (simplified) java code you are executing to get that exception.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jul 02, 2004 3:36 am 
Beginner
Beginner

Joined: Thu Oct 09, 2003 11:41 am
Posts: 39
Location: Paris, France
Okay, here is the mapping

Code:
<hibernate-mapping auto-import="false">
    <class name="User" table="core_user">
        <id name="id" column="user_id" type="java.lang.Long">
            <generator class="increment"/>
        </id>
        <property name="firstName" column="user_firstname" type="string"/>
        <property name="lastName" column="user_lastname" type="string"/>
        <property name="email" column="user_email" type="string" not-null="true" unique="true"/>
        <property name="login" column="user_login" type="string" not-null="true" unique="true"/>
        <property name="passwd" column="user_passwd" not-null="true" type="string"/>

        <many-to-one name="layoutCurrentPage" class="settings.LayoutPage" column="layout_current_page_id" cascade="all"/>
</class>
</hibernate-mapping>

<hibernate-mapping auto-import="false">

    <class name="settings.LayoutPage" table="settings_layout_page">
        <id name="id" column="id" type="java.lang.Long">
            <generator class="increment"/>
        </id>
        <property name="title" column="title" type="string"/>

        <bag name="columns" inverse="true" lazy="false" order-by="column_number asc" cascade="all-delete-orphan">
            <key column="page_id"/>
            <one-to-many class="settings.LayoutColumn"/>
        </bag>

        <many-to-one name="userId" class="User" column="user_id"/>
    </class>

</hibernate-mapping>


Here is the offending code (struts):

Code:
ActionErrors errors = new ActionErrors();
        String pageId = (String) request.getParameter("pageId");
        String title = (String) request.getParameter("title");
        HttpSession sess = request.getSession();
        Session session = HibernateSession.currentSession();
        Transaction tx = session.beginTransaction();
        LayoutManager layoutManager = (LayoutManager) sess.getAttribute(LayoutManager.LAYOUT_SESSION_KEY);
        User me = (User) sess.getAttribute(Constants.USER_SESSION_KEY);
        if (null == pageId)
        {
            //creating a new page after the last page - it works
            LayoutPage lPLast = layoutManager.getLastPage();
            layoutManager.createNewPage(title, lPLast.getPageNumber() + 1);
        }
        else
        {
            try
            {
                LayoutPage lP = (LayoutPage) session.load(LayoutPage.class, new Long(pageId));
// logger displays the accurate data
                logger.info("Loaded Page Id == " + pageId);
                logger.info("Page Title == " + lP.getTitle());
                me.setLayoutCurrentPage(lP);
                session.update(me);
                tx.commit();
            }
            catch (NumberFormatException e)
            {
//just in case pageId is corrupted...
                tx.rollback();
                errors.add(ActionErrors.GLOBAL_ERROR, new ActionError("error.ID.numberformat"));
            }
        }
         HibernateSession.closeSession();
        return (mapping.findForward("success"));



If the method is called with pageId set to a not-null value, then it means the user is trying to go to a page that already exists. If the action is invoked with no pageId, the action creates a new page but the user stays on the current page (this can be changed, but is irrelevant right now).


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jul 02, 2004 3:37 am 
Beginner
Beginner

Joined: Thu Oct 09, 2003 11:41 am
Posts: 39
Location: Paris, France
The exception is thrown when sessiopn.update(me) is executing


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jul 02, 2004 4:10 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 3:00 pm
Posts: 1816
Location: Austin, TX
Change:
Code:

                LayoutPage lP = (LayoutPage) session.load(LayoutPage.class, new Long(pageId));
                me.setLayoutCurrentPage(lP);
                session.update(me);
                tx.commit();


to:
Code:

                session.lock(me, LockMode.READ);
                LayoutPage lP = (LayoutPage) session.load(LayoutPage.class, new Long(pageId));
                me.setLayoutCurrentPage(lP);
                tx.commit();


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jul 02, 2004 5:02 am 
Beginner
Beginner

Joined: Thu Oct 09, 2003 11:41 am
Posts: 39
Location: Paris, France
Okay, cool, it works.

What's the logic behind it?


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jul 02, 2004 11:53 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 3:00 pm
Posts: 1816
Location: Austin, TX
The message "a different object with the same identifier" really means that a different instance of an entity with the same identifier as the instance you are trying to update already existed as a persistent instance in the session. You have an instance of User which is in a transient state (the one you stored in http session). You then open a session and load a LayoutPage instance, which in turn must load (as an association) the same User entity (same id, but different instance).

When you call update on that transient instance, Hibernate is going to check whether a seperate instance of the same entity is already associated with the current session, and throw that error if true. In this case this has to be true to get that error. By locking (reassociating) the transient instance first, you avoid that problem.

For completness, you could also have called session.update(me) first and this would have worked, just as calling session.lock() after the load would also have failed.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Aug 19, 2004 3:16 pm 
Newbie

Joined: Thu Aug 19, 2004 7:57 am
Posts: 14
steve wrote:
The message "a different object with the same identifier" really means that a different instance of an entity with the same identifier as the instance you are trying to update already existed as a persistent instance in the session. You have an instance of User which is in a transient state (the one you stored in http session). You then open a session and load a LayoutPage instance, which in turn must load (as an association) the same User entity (same id, but different instance).

When you call update on that transient instance, Hibernate is going to check whether a seperate instance of the same entity is already associated with the current session, and throw that error if true. In this case this has to be true to get that error. By locking (reassociating) the transient instance first, you avoid that problem.

For completness, you could also have called session.update(me) first and this would have worked, just as calling session.lock() after the load would also have failed.


Steve,

It sounds like you know what you're talking baout here, but I can't seem to interperate your thoughts here and apply them to my problem. Are you able to help with my problem?

http://forum.hibernate.org/viewtopic.php?t=933689

_________________
Andy Czerwonka


Top
 Profile  
 
 Post subject:
PostPosted: Thu Oct 07, 2004 4:51 pm 
Newbie

Joined: Tue Oct 05, 2004 10:42 am
Posts: 3
steve wrote:
The message "a different object with the same identifier" really means that a different instance of an entity with the same identifier as the instance you are trying to update already existed as a persistent instance in the session. You have an instance of User which is in a transient state (the one you stored in http session). You then open a session and load a LayoutPage instance, which in turn must load (as an association) the same User entity (same id, but different instance).

When you call update on that transient instance, Hibernate is going to check whether a seperate instance of the same entity is already associated with the current session, and throw that error if true. In this case this has to be true to get that error. By locking (reassociating) the transient instance first, you avoid that problem.

For completness, you could also have called session.update(me) first and this would have worked, just as calling session.lock() after the load would also have failed.


I have studied the above over the last couple of days, trying to absorb it, since it seems to hold the key to the prolems I am having, but I am still stuck.

In my case, I have two objects, one of class QPXServerDef, the other UserPrefs. The prefs objects hold a list of server def objects, and also a reference to the currently selected server def. If I try to set the prefs' selected server to an object that is also on the prefs' list of servers, I get a NonUniqueObjectException no matter which way I try to lock() the prefs and server object or do session updates() ahead of time.

The following are simplified versions of my mapping file, class definitions and the problem code block.

Thanks in advance for any help you can give.
Yours, JonTom


Code:

<hibernate-mapping package="com.itasoftware.rfd.hibernate">
 
  <class
    name="QPXServerDef"
    table="QPXServer">
   
    <id name="ID"
      column="ID"
      unsaved-value="null">
      <generator class="increment"/>
    </id>
   
    <property name="name"
      column="QPXName"
      not-null="true"/>

    <many-to-one name="userPrefs" class="UserPrefs" column="UserPrefsID" not-null="false"/>

  </class>

  <class
    name="UserPrefs"
    table="UserPrefs">
   
    <id name="ID"
      column="ID"
      unsaved-value="null">
      <generator class="increment"/>
    </id>

    <many-to-one name="selectedQPXServer"
      class="QPXServerDef"
      column="SelectedQPXID"
      cascade="all"/>
   
    <!-- Inverse attr is not needed if QPXServerDef does not need to reference UserPrefs -->
    <set name="QPXServers" inverse="true" lazy="true" cascade="all">
      <key column="UserPrefsID"/>
      <one-to-many class="QPXServerDef"/>
    </set>

  </class>
 
</hibernate-mapping>



public class QPXServerDef
  implements Serializable
{

  // Constructor
  public QPXServerDef()
  {
    super();
    sLog.trace("QPXServerDef: constructor");
  }

  ////////////////////////////////////////////////////////////
  // Property methods

  public final Long getID() {
    return mID;
  }
  public void setID(Long value) {
    mID = value;
  }

  public final String getName() {
    return mName;
  }
  public void setName(String value) {
    mName = value;
  }

  public final UserPrefs getUserPrefs() {
    return mUserPrefs;
  }
  public void setUserPrefs(UserPrefs value) {
    mUserPrefs = value;
  }

  protected Long mID;
  protected String mName;
  protected UserPrefs mUserPrefs;
}


public class UserPrefs
  implements Serializable
{

  // Constructor
  public UserPrefs()
  {
    super();
  }

  public final Long getID() {
    return mID;
  }
  public void setID(Long value) {
    mID = value;
  }

  public final Set getQPXServers() {
    return mQPXServers;
  }
  public void setQPXServers(Set value) {
    mQPXServers = value;
  }

  public final QPXServerDef getSelectedQPXServer() {
    return mSelectedQPXServer;
  }
  public void setSelectedQPXServer(QPXServerDef value) {
    mSelectedQPXServer = value;
  }

  protected Long mID;
  private QPXServerDef mSelectedQPXServer;
  private Set mQPXServers;
}



      Session hibSession = null;
      Transaction tx = null;
      try {
   hibSession = HIBUtil.newSession();
   tx = hibSession.beginTransaction();

   hibSession.lock(prefs, LockMode.READ);
   hibSession.lock(qpxDef, LockMode.READ);
   prefs.setSelectedQPXServer(qpxDef);

   tx.commit();
      }
      catch (HibernateException except) {
   HIBUtil.rollbackTx(tx);
   sLog.error("HIBError: " + except.getMessage(), except);
   throw new HIBException(except);
      }
      finally {
   HIBUtil.shutSession(hibSession);
      }



Top
 Profile  
 
 Post subject:
PostPosted: Wed Oct 13, 2004 3:25 pm 
Regular
Regular

Joined: Wed Jun 30, 2004 4:02 pm
Posts: 64
I am having the same type of problem.

I tried adding the lock as above and got:

net.sf.hibernate.HibernateException: cannot lock an unsaved transient instance

Ok, that makes sense since I am creating a new object to insert. I read the faq: http://www.hibernate.org/117.html#A23

Quote:
Hibernate throws: Another object was associated with this id

For a particular session, there may be at most one object that represents a particular database row. So, if you already loaded an object with a particular identifier, you can't then try to associate a different object with the same identifier by calling update().

If you absolutely must do this, evict() the original instance first - but it is much better to simply avoid this situation.


I wish the FAQ would give some hints about how to avoid this situation. In my app I am having this problem in 2 places. I'm thinking that it isn't a coincidence that both are mapped as many to many.

Code:
        <set
            name="targetMarkets"
            table="PRODUCT_TARGET_MARKET"
            lazy="false"
            inverse="false"
            cascade="save-update"
            sort="unsorted"
        >

              <key
                  column="PRODUCT_ID"
              >
              </key>

              <many-to-many
                  class="com.acme.model.TargetMarket"
                  column="TARGET_MARKET_ID"
                  outer-join="auto"
               />

        </set>



Anyone have any ideas?


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 23 posts ]  Go to page 1, 2  Next

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:
cron
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.