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