-->
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: Not updating parent point in many-to-one
PostPosted: Fri Feb 13, 2004 10:10 pm 
Beginner
Beginner

Joined: Tue Sep 09, 2003 9:11 pm
Posts: 32
ok - I figure I'm doing something wrong but here goes:

With the following code, I expect that 1 Sale object and 2 Item
objects will be saved to the database. Also, the 2 Items will
each have their parent order (field orderID) set to the Sale object.

This doesn't seem to work. If I load the first product from
the database and change anything on it, then a query is issued
during the flush process to set the orderID=null on the first
Item object.

The queries issued to the database are:
1. insert for sale
3. insert into item for item1
4. update to product for p1
5. update item set orderID=null where orderID=? and lineID=? [for item1]
6. inset into item for item2

I'm not sure why the 5th statement is being generated. Any thoughts?
I tried to summarize what I was seeing in the log, but I can always post
more from the actually log if necessary....

When I remove the statement p1.setInvUnitPrice(Money.dollars(15.25)), the
first item is updated correctly so that the orderID field points to the
Sale object.

Figures I find this on a Friday night.....

Thanks in advance,
Chris....


Code:
   public static void main(String[] args) throws Exception  {

      //context contains the Session object which
      //the DAO object retrieves in order to perform the lookup
      //query
      Context context = new Context();

      Sale sale = new Sale();

      context.getSession().save(sale);

      Item item1 = new Item();

      ProductDAO pd = new ProductDAO();

      Product p1 = pd.loadBySKU(context, "widg1");
      item1.setProduct(p1);
      sale.addItem(item1);

      p1.setInvUnitPrice(Money.dollars(15.25));

      Item item2 = new Item();
      Product p2 = pd.loadBySKU(context, "widg2");
      item2.setProduct(p2);
      sale.addItem(item2);

      context.getSession().flush();
      context.getSession().connection().commit();
   }



Code:
   <class name="com.itsolut.entity.order.IOrder"
      table="order" discriminator-value="X" proxy="com.itsolut.entity.order.IOrder" >
      <id name="orderID" type="long" unsaved-value="0">
         <generator class="identity"/>
      </id>
      <discriminator column="type" type="string" length="2" />

      <set name="items" lazy="true" cascade="save-update">
         <key column="orderID"/>
         <one-to-many class="com.itsolut.entity.order.Item"/>
      </set>
      <subclass name="com.itsolut.entity.order.Sale" discriminator-value="S"
         proxy="com.itsolut.entity.order.Sale"/>
   </class>

   <class name="com.itsolut.entity.order.Item" table="order_item"
      proxy="com.itsolut.entity.order.Item">
      <id name="lineID" type="long" unsaved-value="0">
         <generator class="identity"/>
      </id>
      <many-to-one name="product" class="com.itsolut.entity.product.Product" column="productID"/>
      <property name="inventoryUnitPrice" type="com.itsolut.core.hibernate.type.MoneyType">
         <column name="invPrice"/>
         <column name="invCurrency"/>
      </property>
      <many-to-one name="order" column="orderID"
         class="com.itsolut.entity.order.IOrder"/>
   </class>

   <class name="com.itsolut.entity.product.Product" table="product"
      proxy="com.itsolut.entity.product.Product" discriminator-value="X">
      <id name="prodID" type="long" unsaved-value="0">
         <generator class="identity"/>
      </id>
      <discriminator column="type" type="char" length="1"/>
         <subclass name="com.itsolut.entity.product.Inventorable" discriminator-value="I"
            proxy="com.itsolut.entity.product.Inventorable">
            <property name="invUnitPrice" type="com.itsolut.core.hibernate.type.MoneyType">
               <column name="invMoney"/>
               <column name="invCurrency"/>
            </property>
         </subclass>
   </class>


Top
 Profile  
 
 Post subject:
PostPosted: Sat Feb 14, 2004 6:03 pm 
Senior
Senior

Joined: Sun Jan 04, 2004 2:46 pm
Posts: 147
Ok looks like the problem is a missing inverse='true' in one direction of your relationship. There's a nice chapter in the doco about parent/child relationships but in short to update a bidirectional relationship ( set on one side, many-to-one property on the other ) do the following

Code:
sale.addItem(item1);
item1.setOrder(sale);


At the moment hibernate sees the new item in sale so insert it ( query 3 ), then looks at item and see's it's sale/order member isn't set so it goes back and removes the relationship ( query 5 ). Adding the link in the other direction should fix your problem but will probably result in an additional redundant query. So add inverse='true' to the item set in the IOrder class and it should all work fine with the minimum of queries.

Setting inverse to true basically says don't use this end of the relationship to reflect changes into the database ( ie don't do it twice ). However it is still important to update both ends of the relationship in the code itself for consistency.

Cheers.

Myk.


Top
 Profile  
 
 Post subject:
PostPosted: Sun Feb 15, 2004 11:02 pm 
Beginner
Beginner

Joined: Tue Sep 09, 2003 9:11 pm
Posts: 32
Myk,

Thanks for the reply. As hard as I tried to get the example right, I missed the addItem code which does exactly what you suggesetd (adds the item to the order item Set and sets the item.setOrder(this).

It turns out after some digging that there was something going on with my overridden equals/hash methods. It appears that if I update a property on the Product after I have added the Item to the order, then the order.items collection is flagged as dirty after the initial item insert.

Not entirely sure of everything that is going on in the Hibernate code but basically what I'm seeing is:
1. insert into item
2. update to product
3. Then there is a dirty check on the collection which calls:
set.contains(Item)

The problem appears to be that the hashcode when the item is originally added to the set is 0. After the item is saved to the database, the hashcode changes since part of the hashcode is the itemID. I didn't spend a lot of time analyzing set.contains but it looks like the backing Map uses hashcode to set the Item into a bucket. When the hashcode changes, the set.contains looks into a different bucket and doesn't find the Item. Now, this isn't entirely accurate; but it's probably close....

Anyway, I took out the equals/hash method since the reason they were added in the first place is no longer valid. This problem went away...

Thanks again,
Chris....


Top
 Profile  
 
 Post subject:
PostPosted: Sun Feb 15, 2004 11:03 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
You should read the JavaDoc for the Java collections API and understand how HashMaps work.


Top
 Profile  
 
 Post subject:
PostPosted: Sun Feb 15, 2004 11:39 pm 
Beginner
Beginner

Joined: Tue Sep 09, 2003 9:11 pm
Posts: 32
Thanks I did and read some of the info on the wiki!

Chris....


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.