-->
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: parent INSERT, child UPDATE ???? on session.save
PostPosted: Wed Aug 09, 2006 1:46 pm 
Newbie

Joined: Mon Jul 24, 2006 8:55 pm
Posts: 6
This is a simple example of a one-to-many parent/child relationship. The parent is an Order and the children are OrderItems.

When I run a test to save an Order and a set of OrderItems, the generated SQL shows an INSERT for the Order parent and an UPDATE ! for the child OrderItems.

The Order XML
Code:
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
   
<hibernate-mapping>
    <class name="tables.Order" table="orders">
        <id name="id" type="integer" unsaved-value="null" >
            <column name="id" sql-type="integer"
                    not-null="true"/>
        </id>
       
        <property name="date">
            <column name="order_date" sql-type="datetime" not-null="true"/>
        </property>

        <property name="priceTotal">
            <column name="price_total" sql-type="double" not-null="true"/>
        </property>
       
        <set name="orderItems" table="order_items" inverse="true" cascade="all">
           <key column="order_id" />
           <one-to-many class="tables.OrderItem" />
        </set>
               
    </class>

</hibernate-mapping>   


The OrderItem XML
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="tables.OrderItem" table="order_items">

        <id name="id" type="integer" unsaved-value="null" >
            <column name="id" sql-type="integer" not-null="true"/>
        </id>
       
        <property name="orderId" insert="false" update="false">
            <column name="order_id" sql-type="integer" not-null="true"/>
        </property>

        <property name="productId">
            <column name="product_id" sql-type="integer" not-null="true"/>
        </property>
       
        <property name="amount">
            <column name="amount" sql-type="integer" not-null="true"/>
        </property>

        <property name="price">
            <column name="price" sql-type="double" not-null="true"/>
        </property>

      <many-to-one name="order" class="tables.Order" column="order_id"
         not-null="true" cascade="save-update" />
      
   </class>

</hibernate-mapping>   


The Order class
Code:
public class Order {
   private int id;
   private Date date;
   private double priceTotal;
   private Set orderItems = new HashSet();
   
   public Order() {
      this.date = new Date();
   }
   
   public void addOrderItem (OrderItem oi)
   {
      this.priceTotal = this.priceTotal + oi.getAmount();
      this.orderItems.add(oi);
   }
   
   public Date getDate() {
      return date;
   }

   public int getId() {
      return id;
   }
   public Set getOrderItems() {
      return orderItems;
   }
   public double getPriceTotal() {
      return priceTotal;
   }
   public void setDate(Date date) {
      this.date = date;
   }
   public void setId(int value) {
      id = value;
   }
   public void setOrderItems(Set set) {
      orderItems = set;
   }
   public void setPriceTotal(double d) {
      priceTotal = d;
   }
   
   public String toString() {
      String s = "[Order] id=" + id +
                 "\n        priceTotal=" + priceTotal +
                 "\n        date=" + date + "\n";
      s = s + "Order items:\n";
      Iterator i = orderItems.iterator();
      String o = "";
      while (i.hasNext()) {
            OrderItem item = (OrderItem) i.next();
            o = o + "  " + item + "\n";
        }
       
        return s + o;
   }

}



The OrderItem class
Code:
public class OrderItem {

   /**
    * Empty constructor to conform to JavaBeans convention.
    *
    */
   public OrderItem() {
      // empty default constructor
   }

   //   constructor that sets parent order
   public OrderItem(Order order)
      {this.order = order;}   
   
   private int id;
   private Order order;
   private int productId;
   private int orderId;
   private double price;
   private int amount;
   
   public int getAmount() {
      return amount;
   }
   public int getId() {
      return id;
   }
   public Order getOrder() {
      return order;
   }
   public int getOrderId() {
      return orderId;
   }
   public double getPrice() {
      return price;
   }
   public int getProductId() {
      return productId;
   }
   public void setAmount(int i) {
      amount = i;
   }
   public void setId(int value) {
      id = value;
   }
   public void setOrder(Order order) {
      this.order = order;
   }
   public void setOrderId(int value) {
      this.orderId = value;
   }
   public void setPrice(double d) {
      price = d;
   }
   public void setProductId(int value) {
      this.productId = value;
   }

   public String toString() {
      return "[OrderItem] id=" + id +
             "\n             amount=" + amount +
             "\n             price=" + price +
             "\n             Order Id" + orderId +
             "\n             Product Id " + productId;
   }

}


The test method
Code:
    public void createOrderAndItems4()
    {
      Session session = null;
      Transaction tx = null;
      try
      {
         session = HibernateUtil.currentSession();
         tx = session.beginTransaction();
         
         OrderItem oi1 = new OrderItem();
         oi1.setProductId(1);
         oi1.setAmount(5);
         oi1.setPrice(25.25);
         
         Order order = new Order();
         order.getOrderItems().add(oi1);
         
         oi1.setOrder(order);
         
         System.out.println("**  before Order save");
         System.out.println(order);
         
         session.save(order);
         System.out.println("**  after Order save");
         session.flush();
         System.out.println("**  after session flush");
         
         tx.commit();
         HibernateUtil.closeSession();
     
      }
      catch (HibernateException e) {
         e.printStackTrace();
         if (tx != null && tx.isActive())
            tx.rollback();
      }   
    }


A snippet of the log file
Code:
**  after Order save
11:50:05,692 DEBUG SQL:346 - insert into orders (order_date, price_total, id) values (?, ?, ?)
Hibernate: insert into orders (order_date, price_total, id) values (?, ?, ?)
11:50:05,863 DEBUG SQL:346 - update order_items set product_id=?, amount=?, price=?, order_id=? where id=?
Hibernate: update order_items set product_id=?, amount=?, price=?, order_id=? where id=?
11:50:06,003 ERROR AbstractBatcher:61 - Exception executing batch:
org.hibernate.StaleStateException: Batch update returned unexpected row count from update: 0 actual row count: 0 expected: 1


The error makes sense to me, there is no row in the table to update. But why is Hibernate generating an UPDATE for the order_items ?

By the way
There is nothing fatally wrong with the mapping to the database which is MySQL. I can insert a single Order or a single OrderItem without any problem.

Thanks for your attention,
Rich Morrison


Top
 Profile  
 
 Post subject:
PostPosted: Wed Aug 09, 2006 2:51 pm 
Expert
Expert

Joined: Tue Apr 25, 2006 12:04 pm
Posts: 260
I changed the mapping file for OrderItem a bit to see what happens with your testcase. The change is moving insert="false" and update="false" onto <many-to-one> element from property 'orderId'.


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="tables.OrderItem" table="order_items">

        <id name="id" type="integer" unsaved-value="null" >
            <column name="id" sql-type="integer" not-null="true"/>
        </id>
       
        <property name="orderId">
            <column name="order_id" sql-type="integer" not-null="true"/>
        </property>

        <property name="productId">
            <column name="product_id" sql-type="integer" not-null="true"/>
        </property>
       
        <property name="amount">
            <column name="amount" sql-type="integer" not-null="true"/>
        </property>

        <property name="price">
            <column name="price" sql-type="double" not-null="true"/>
        </property>

      <many-to-one name="order" class="tables.Order" column="order_id" not-null="true" cascade="save-update"  insert="false" update="false"/>
       
   </class>

</hibernate-mapping>


Top
 Profile  
 
 Post subject:
PostPosted: Wed Aug 09, 2006 5:35 pm 
Newbie

Joined: Mon Jul 24, 2006 8:55 pm
Posts: 6
Thanks for the reply.

Unfortunately that didn't do the trick. Hibernate still generates an UPDATE for the OrderItem.

Any other suggestions ?

RichM


Top
 Profile  
 
 Post subject:
PostPosted: Thu Aug 10, 2006 7:40 am 
Regular
Regular

Joined: Tue May 16, 2006 3:32 am
Posts: 117
When a parent is saved, all its children are passed to saveorupdate. Hibernate has to decide whether to call insert or update for each of them. The criteria it uses is based on the unsaved-value. In your case you have unsaved-value="null" i.e. you are telling Hibernate that if the object has its id as null then insert else update. Since you have already set the id, the objects are always updated. Use a generator class for setting the id. If you want to assign ids then you may need to the use the unsaved-value of version or timestamp or implement Interceptor.isUnsaved().


Top
 Profile  
 
 Post subject:
PostPosted: Thu Aug 10, 2006 9:17 am 
Newbie

Joined: Mon Jul 24, 2006 8:55 pm
Posts: 6
JayeshJ,

Thanks for the reply. I understand what you are saying, actually have read the manual :)

But it looks to me that the primary key for OrderItem is not set in the test code. In fact the primary key is auto-incremented my MySql.

The foreign key of order_id is not set in the test code either.

But I think I see a problem:
in the OrderItem class, the getter for orderId returns a property in the OrderItem class. I think it should return the orderId of the parent Order.

Does that seem right ?

Thanks,
RichM


Top
 Profile  
 
 Post subject:
PostPosted: Thu Aug 10, 2006 2:09 pm 
Senior
Senior

Joined: Sat Nov 27, 2004 4:13 am
Posts: 137
Primitives can not be NULL, so your unsaved-value condition can never be satisfied.

I recomand to change IDs in both classes to Integer instead of int; you can also change unsaved-value='0'. But I prefer the first choice.
-----------------------

DON'T FORGET THE CREDITS

_________________
don't forget to credit!

Amir Pashazadeh
Payeshgaran MT
پايشگران مديريت طرح
http://www.payeshgaran.co
http://www.payeshgaran.org
http://www.payeshgaran.net


Top
 Profile  
 
 Post subject:
PostPosted: Fri Aug 11, 2006 3:40 pm 
Newbie

Joined: Mon Jul 24, 2006 8:55 pm
Posts: 6
Pasha,

Thank you. That did it.

I remember seeing that in the documentation but it didn't click at the time.

RichM


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.