-->
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.  [ 7 posts ] 
Author Message
 Post subject: one-to-many cascade saveOrUpdate()
PostPosted: Tue Apr 20, 2004 10:34 am 
Senior
Senior

Joined: Wed Mar 24, 2004 11:40 am
Posts: 146
Location: Indianapolis, IN, USA
I am using Hibernate 2.1.2.

I have the following object structure

Code:
class Foo
{
  public Long fooId;
  ...
  public List barList;
}

class Bar
{
  public BarCompositeKey;
  ...
}

class BarCompositeKey
{
  public Long fooId;
  public Long barId;

  public boolean equals(Object object)
  {
    logger.debug("begin executing equals()");
    boolean match = false;
    if (object instanceof BarCompositeKey)
    {
      logger.debug("object is an instance of BarCompositeKey");
      BarCompositeKey barCompositeKey = (BarCompositeKey) object;
      if (barCompositeKey.getFooId() != null &&
          barCompositeKey.getBarId() != null &&
          barCompositeKey.getFooId().equals(this.fooId) &&
          barCompositeKey.getBarId().equals(this.barId)
          )
      {
        match = true;
        logger.debug("match is true");
      }
    }
    return match;
  }

  public int hashCode()
  {
    logger.debug("begin executing hashCode()");
    int hashValue = 7;
    logger.debug("hashValue: " + hashValue);
    if (fooId != null)
    {
      logger.debug("fooId is not null");
      hashValue = (11 * hashValue) + fooId.hashCode();
      logger.debug("hashValue: " + hashValue);
    }

    if (barId != null)
    {
      logger.debug("barId is not null");
      hashValue = (11 * hashValue) + barId.hashCode();
      logger.debug("hashValue: " + hashValue);
    }
    logger.debug("final hashValue: " + hashValue);
    return hashValue;
  }
}


The corresponding relationship structure is as follows:

Code:
<hibernate-mapping>
  <class name="Foo" table="FOO" dynamic-update="false" dynamic-insert="false">

    <id name="fooId" column="FOOID" type="java.lang.Long" unsaved-value="null">
      <generator class="sequence">
        <param name="sequence">SEQ_FOOID</param>
      </generator>
     </id>
    ...
    ...
    <bag name="barList" lazy="false" inverse="false" cascade="save-update">
      <key column="BARID"/>
      <one-to-many class="Bar"/>
    </bag>
  </class>
</hibernate-mapping>

<hibernate-mapping>
  <class name="Bar" table="BAR" dynamic-update="false" dynamic-insert="false">
    <composite-id name="barCompositeKey" class="BarCompositeKey" unsaved-value="none">
      <key-property name="fooId" type="java.lang.Long" column="FOOID"/>
      <key-property name="barId" type="java.lang.Long" column="BARID"/>
    </composite-id>
    ...
    ...
  </class>
</hibernate-mapping>


When I call saveOrUpdate() on the Foo object, it correctly determines whether to save or update based on the value of the Id attribute. However, when it has to save the list of Bar objects I am getting a HibernateException. Here is a section of log trace. I have added debug statements into my hashCode() and equals() methods in the BarCompositeKey object that are also listed.


The following are the issues I have.

    1. Why is hashCode() called so many times even though there is only one item in the barList?
    2. Since fooId is populated into the Foo object by using a sequence SEQ_FOOID, once Foo is saved, is the fooId value automatically populated in the BarCompositeKey for saving the Bar objects? Note: The barId value is always populated as it is sort of like the lineNumber and so the BarCompositeKey is never null.
    3. What could be causing the composite key to get modified and throw the HibernateException?

Any help is greatly appreciated.

    DEBUG [net.sf.hibernate.engine.Cascades] - processing cascades for: Foo
    DEBUG [net.sf.hibernate.engine.Cascades] - cascading to collection: Foo.barList
    DEBUG [net.sf.hibernate.engine.Cascades] - cascading to saveOrUpdate()
    DEBUG [net.sf.hibernate.engine.Cascades] - id unsaved-value strategy NONE
    DEBUG [BarCompositeKey] - begin executing hashCode()
    DEBUG [BarCompositeKey] - hashValue: 7
    DEBUG [BarCompositeKey] - barId is not null
    DEBUG [BarCompositeKey] - hashValue: 78
    DEBUG [BarCompositeKey] - final hashValue: 78
    DEBUG [net.sf.hibernate.impl.SessionImpl] - saveOrUpdate() previously saved instance with id: BarCompositeKey@4e
    DEBUG [BarCompositeKey] - begin executing hashCode()
    DEBUG [BarCompositeKey] - hashValue: 7
    DEBUG [BarCompositeKey] - barId is not null
    DEBUG [BarCompositeKey] - hashValue: 78
    DEBUG [BarCompositeKey] - final hashValue: 78
    DEBUG [net.sf.hibernate.impl.SessionImpl] - updating [Bar#BarCompositeKey@4e]
    DEBUG [BarCompositeKey] - begin executing hashCode()
    DEBUG [BarCompositeKey] - hashValue: 7
    DEBUG [BarCompositeKey] - barId is not null
    DEBUG [BarCompositeKey] - hashValue: 78
    DEBUG [BarCompositeKey] - final hashValue: 78
    DEBUG [BarCompositeKey] - begin executing hashCode()
    DEBUG [BarCompositeKey] - hashValue: 7
    DEBUG [BarCompositeKey] - barId is not null
    DEBUG [BarCompositeKey] - hashValue: 78
    DEBUG [BarCompositeKey] - final hashValue: 78
    DEBUG [BarCompositeKey] - begin executing hashCode()
    DEBUG [BarCompositeKey] - hashValue: 7
    DEBUG [BarCompositeKey] - barId is not null
    DEBUG [BarCompositeKey] - hashValue: 78
    DEBUG [BarCompositeKey] - final hashValue: 78
    DEBUG [net.sf.hibernate.engine.Cascades] - done processing cascades for: Foo
    DEBUG [net.sf.hibernate.transaction.JDBCTransaction] - commit
    DEBUG [net.sf.hibernate.impl.SessionImpl] - flushing session
    DEBUG [net.sf.hibernate.engine.Cascades] - processing cascades for: Foo
    DEBUG [net.sf.hibernate.engine.Cascades] - cascading to saveOrUpdate()
    DEBUG [net.sf.hibernate.impl.SessionImpl] - saveOrUpdate() persistent instance
    DEBUG [net.sf.hibernate.engine.Cascades] - cascading to collection: Foo.barList
    DEBUG [net.sf.hibernate.engine.Cascades] - cascading to saveOrUpdate()
    DEBUG [net.sf.hibernate.impl.SessionImpl] - saveOrUpdate() persistent instance
    DEBUG [net.sf.hibernate.engine.Cascades] - done processing cascades for: Foo
    DEBUG [net.sf.hibernate.impl.SessionImpl] - Flushing entities and processing referenced collections
    DEBUG [net.sf.hibernate.impl.WrapVisitor] - Wrapped collection in role: Foo.barList
    DEBUG [net.sf.hibernate.impl.SessionImpl] - Collection found: [barList#19], was: [<unreferenced>]
    DEBUG [BarCompositeKey] - begin executing equals()
    DEBUG [BarCompositeKey] - object is an instance of BarCompositeKey
    DEBUG [BarCompositeKey] - begin executing hashCode()
    DEBUG [BarCompositeKey] - hashValue: 7
    DEBUG [BarCompositeKey] - barId is not null
    DEBUG [BarCompositeKey] - hashValue: 78
    DEBUG [BarCompositeKey] - final hashValue: 78
    DEBUG [BarCompositeKey] - begin executing hashCode()
    DEBUG [BarCompositeKey] - hashValue: 7
    DEBUG [BarCompositeKey] - barId is not null
    DEBUG [BarCompositeKey] - hashValue: 78
    DEBUG [BarCompositeKey] - final hashValue: 78
    DEBUG [net.sf.hibernate.transaction.JDBCTransaction] - rollback
    DEBUG [net.sf.hibernate.impl.SessionImpl] - transaction completion
    DEBUG [net.sf.hibernate.transaction.JDBCTransaction] - re-enabling autocommit
    WARN [FooDAO] - Hibernate Exception in save(Foo@1318953):
    Stack Trace:
    net.sf.hibernate.HibernateException: identifier of an instance of Bar altered from BarCompositeKey@4e to BarCompositeKey@4e
    at net.sf.hibernate.impl.SessionImpl.checkId(SessionImpl.java:2606)
    at net.sf.hibernate.impl.SessionImpl.flushEntity(SessionImpl.java:2429)
    at net.sf.hibernate.impl.SessionImpl.flushEntities(SessionImpl.java:2422)
    at net.sf.hibernate.impl.SessionImpl.flushEverything(SessionImpl.java:2224)
    at net.sf.hibernate.impl.SessionImpl.flush(SessionImpl.java:2203)
    at net.sf.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:61)
    at FooDAO.save(Unknown Source)
    at FooCompleteAction.add(Unknown Source)
    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:324)
    at org.apache.struts.actions.DispatchAction.dispatchMethod(DispatchAction.java:280)
    at org.apache.struts.actions.LookupDispatchAction.execute(LookupDispatchAction.java:252)
    at org.apache.struts.action.RequestProcessor.processActionPerform(RequestProcessor.java:484)
    at org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:274)
    at org.apache.struts.action.ActionServlet.process(ActionServlet.java:1482)
    at org.apache.struts.action.ActionServlet.doGet(ActionServlet.java:507)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:126)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:103)
    at com.caucho.server.http.FilterChainServlet.doFilter(FilterChainServlet.java:96)
    at com.caucho.server.http.Invocation.service(Invocation.java:315)
    at com.caucho.server.http.RunnerRequest.handleRequest(RunnerRequest.java:346)
    at com.caucho.server.http.RunnerRequest.handleConnection(RunnerRequest.java:274)
    at com.caucho.server.TcpConnection.run(TcpConnection.java:139)
    at java.lang.Thread.run(Thread.java:534)


Top
 Profile  
 
 Post subject:
PostPosted: Tue Apr 20, 2004 5:12 pm 
Senior
Senior

Joined: Wed Mar 24, 2004 11:40 am
Posts: 146
Location: Indianapolis, IN, USA
Do I have to save the Foo object first without the associated barList and then append the fooId to the barCompositeKey for each Bar object and save them?

TIA


Top
 Profile  
 
 Post subject:
PostPosted: Tue Apr 20, 2004 6:15 pm 
Regular
Regular

Joined: Tue Dec 02, 2003 6:25 pm
Posts: 61
Location: Dallas, TX
I should make the caveat that I have not used composite keys and I avoid them as much as possible. If you have control over your DB design I would recommend changing it to have a single primary or surrogate key.

Quote:
1. Why is hashCode() called so many times even though there is only one item in the barList?

Best guess is that it is being called in a couple of cases merely to generate a log message. It is also being called when Hibernate checks its session cache to see if the object is already there.

Also, I would probably modify my hashCode method to fail if any of the necessary components are null. It doesn't make sense to me to instantiate a composite-id class unless I am going to immediately set all the necessary attributes for hashCode to succeed.

Quote:
2. Since fooId is populated into the Foo object by using a sequence SEQ_FOOID, once Foo is saved, is the fooId value automatically populated in the BarCompositeKey for saving the Bar objects? Note: The barId value is always populated as it is sort of like the lineNumber and so the BarCompositeKey is never null.


I'm surprised you are getting as far as you are. Can you paste in your test code? There must be a place where you are instantiating BarCompositeKey yourself, because Hibernate does not have an IdentifierGenerator for composite keys, and that seemsto be the central problem. It seems like you want Hibernate to update a part of a composite-id.

My guess is that the fooId will be populated in the table (shouldn't your mapping specify that the table is "BAR"? This isn't your actual code, is it?), but not in the object itself. In terms of the object graph, Hibernate manages the many-to-one from Bar to Foo independently of the one-to-many from Foo to Bar. That is why fooId is always null in your test.

You may want to read http://www.hibernate.org/hib_docs/reference/html/parent-child.html

I would recommend setting inverse="true" on barList. Then, in your code, whenever you add a Bar to barList, you will need to instantiate a BarCompositeKey, set its fooId and barId, and then set bar.barCompositeKey to the the key you just created.

Really, though, it seems like it would be better to get rid of the composite-id and have either a surrogate key with the Bar-to-Foo relationship mapped as a regular many-to-one relationship (rather than a part of the identifier), or use the <list> mapping and make BARID the index.

Unfortunately I can't answer exactly why you are getting the exception you are receiving, but I'm pretty sure it has to do with the parent-child stuff.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Apr 20, 2004 6:39 pm 
Senior
Senior

Joined: Wed Mar 24, 2004 11:40 am
Posts: 146
Location: Indianapolis, IN, USA
First of all, I think I made life 10 times harder for myself by going with that stupid Foo and Bar naming convention instead of saying Order and OrderItem. That would have made life a lot easier for everyone. :)

The composite key is used in the following manner. The primary key for the OrderItem is a combination of orderId and lineId. The orderId in the Order table is retrieved using the sequence. The composite key always has the lineId available to it. I couldn't think of any other way to accomplish this other than with the composite-id.

I had read the parent child reference documentation before but I decided not to have the inverse=true because I would never have a situation where I would have to get from the OrderItem to the Order. Access will always be uni-directional from Order to OrderItem.

The explanation for the hashCode() being called multiple times makes sense. It is most probably because of logging in multiple places.

I am instantiating the CompositeKey myself and setting the lineId (barId) to it. I was wondering if saving the object would automatically populate the orderId (fooId) from the Order object into the OrderItem object just like it does for the Order object using the sequence.

I think there has to be a way to accomplish this but unfortunately, I can't seem to figure out how to do this.

What this boils down to is how to use a composite key where one of the entities is generated in the parent object from a sequence?


Top
 Profile  
 
 Post subject:
PostPosted: Tue Apr 20, 2004 6:54 pm 
Regular
Regular

Joined: Tue Dec 02, 2003 6:25 pm
Posts: 61
Location: Dallas, TX
Yeah Hibernate won't do that. If you add a child to a parent collection, you have to set the child's parent as well (or in your case, set the child's parent id in the composite key), and conversely if you set a child's parent, you also have to add it to the parent's collection.

I found the other thread you started where they recommended that you use a composite key, and I saw some of your actual code. I'd recommend that you investigate the <list> mapping as well.

Is there a LINEITEM table in addition to ORDERS and ORDERLINEITEMS e.g. is LINEITEMID a foreign-key to something else? My guess is no.

If not, then your LINEITEMID is more like a list index - it just makes the LINEITEM unique within the collection of lineitems for a particular order - and you may be able to use the <list> mapping. If you do, Hibernate will automatically maintain both ORDERLINEITEMS.ORDERID and ORDERLINEITEMS.LINEITEMID. I think that's what you're looking for.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Apr 21, 2004 9:33 am 
Senior
Senior

Joined: Wed Mar 24, 2004 11:40 am
Posts: 146
Location: Indianapolis, IN, USA
I guess I was being a bit too ambitious expecting Hibernate to populate the parentId in the composite key automatically.

I have tried working with both <list> and <bag> but in both cases I was using the composite key. I think I am going to make the lineItemId into the <id> on the LineItem object instead of using a composite and have a many-to-one from LineItem to Order.

There are only two tables. ORDERS and ORDERLINEITEMS. The LineItem object is used in both the Order and the Cart object. It is a generic object that is just added as a list in different situations.

When you say "
Quote:
If not, then your LINEITEMID is more like a list index - it just makes the LINEITEM unique within the collection of lineitems for a particular order...
", I am guessing that you are alluding to the fact that the "unique" isn't enforced by hibernate and is instead just based on my table structure i.e. Unique constraint on combination of ORDERID, ORDERLINEITEMID in the ORDERLINEITEMS table. I am going to give your suggestion a shot.

I also thought of using the <key-many-to-one> as used in the following example: http://www.xylax.net/hibernate/compositeid.html. Do you think it might be a good idea?


Top
 Profile  
 
 Post subject:
PostPosted: Wed Apr 21, 2004 10:05 am 
Senior
Senior

Joined: Wed Mar 24, 2004 11:40 am
Posts: 146
Location: Indianapolis, IN, USA
Just as a final feedback to anyone reading through this thread, <key-many-to-one> is the way to go in this situation. Hibernate automatically populates the parentId obtained from the sequence to the composite key. Additionally, I set unique="true" for the many-to-one relationship in the composite key object.


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