tags:

views:

35

answers:

1

I am trying to implement parent child relation on traditional Order and Items tables. For this I am using uni-directional one-to-many association but I can make either "save" operation work or the "delete", one at a time.

If I set inverse="false" I can create new order (with items) but can't Delete it because hibernate tries to delete order record first and it fails due to reference in Items table

On the other hand, if I set inverse="true" then Order can be deleted but not created as it tries to create OrderItems record first that fails due to missing reference in Order table (orderId is not null in Items table)

Bi-directional association is also a pain because it leads to circularities in object graphs. Can anyone suggest me a solution?

My hibernate mapping is:

  <class name="Order" table="ORDERS">
    <id column="ID" name="id" type="long">
      <generator class="native" />
    </id>
    <property name="orderDate" type="timestamp" />
    <property name="customer" type="string" />
    <set cascade="all-delete-orphan" name="items" inverse="true" lazy="true">
      <key column="orderId" not-null="true" />
      <one-to-many class="OrderItem" />
    </set>
  </class>

  <class name="Item" table="Items">
    <id column="id" name="itemId" type="long">
      <generator class="native" />
    </id>
    <property name="product" type="string" />
    <property name="quantity" type="int" />
  </class>

Order.java is:

public class Order implements java.io.Serializable {
    private Long id;
    private Date orderDate;
    private String customer;

    private Set<Item> items;

    public Long getId() {
        return id;
    }

    public Date getInvDate() {
        return invDate;
    }

    public String getCustomer() {
        return customerId;
    }

    public Set<Item> getItems() {
        return items;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public void setOrderDate(Date orderDate) {
        this.orderDate= orderDate;
    }

    public void setCustomer(String customer) {
        this.customer = customer;
    }

    public void setItems(Set<Item> items) {
        this.items = items;
    }
}

Item.java

public class Item implements java.io.Serializable {
    private Long itemId;
    private String product;
    private int quantity;

    public Long getItemId() {
        return itemId;
    }

    public String getProduct() {
        return product;
    }

    public int getQuantity() {
        return quantity;
    }

    public void setItemId(Long itemId) {
        this.itemId = itemId;
    }

    public void setProduct(String productId) {
        this.product = product;
    }

    public void setQuantity(int quantity) {
        this.quantity = quantity;
    }
}

OrderManager.java

public class OrderManagerBase {

    public Long save(Order theOrder) throws RemoteException {
        Session session = HbmUtils.getSessionFactory().getCurrentSession();
        Transaction tx = null;
        Long orderId = null;
        try {
            tx = session.beginTransaction();
            session.persist(theOrder);
            tx.commit();
            orderId = theOrder.getId();
        } catch (RuntimeException e) {
            if (tx!=null) 
                tx.rollback();
            throw new RemoteException("Order could not be saved");
        }finally{
            if (session.isOpen())
                session.close();
        }
        return orderId;
    }

    public Long delete(Order order) throws RemoteException {
        Session session = HbmUtils.getSessionFactory().getCurrentSession();
        Transaction tx = null;
        Long orderId = order.getId();
        try {
            tx = session.beginTransaction();
            session.delete(order);
            tx.commit();
        } catch (RuntimeException e) {
            tx.rollback();
            orderId = null;
        }finally{
            if (session.isOpen())
                session.close();
        }
        return orderId;
    }

    public Order getOrder(long cid) throws RemoteException {
        Session session = HbmUtils.getSessionFactory().getCurrentSession();
        Transaction tx = null;
        Order theOrder = null;
        try {
            tx = session.beginTransaction();
            Query q = session.createQuery("from Order as order " +
                    "left outer join fetch order.items " +
                    "where order.id = :id").setReadOnly(true);
            q.setParameter("id", cid);
            theOrder = (Order) q.uniqueResult();
            tx.commit();
        } catch (RuntimeException e) {
            if (tx!=null) 
                tx.rollback();
            e.printStackTrace();
        }finally{
            if (session.isOpen())
                session.close();
        }
        return theOrder;
    }
}

Table are:

CREATE TABLE  Orders (
  id bigint(20) NOT NULL AUTO_INCREMENT,
  orderDate datetime DEFAULT NULL,
  PRIMARY KEY (id),
)

CREATE TABLE  Items (
  id bigint(20) NOT NULL AUTO_INCREMENT,
  orderId` bigint(20) NOT NULL,
  product varchar(50) DEFAULT NULL,
  quantity bigint(20) DEFAULT NULL,
  PRIMARY KEY (id),
  CONSTRAINT FK_orderitems_1 FOREIGN KEY (orderId) REFERENCES Orderss (id)
)
+1  A: 

on parent Order:

<set name="items" access="field" inverse="true" cascade="all" order-by="id">
    <key column="parent_order_id" />
    <one-to-many class="com.example.Item" />
</set>

void addItem(Item item) {
    items.add(item);
    item.setParent(this);
}

on child Item:

<many-to-one name="parent" not-null="true" column="orderId" foreign-key="FK_order_to_item" />

private Order parent;

void setParent(Order order) {
    this.parent = order;
}

Add items via Order#addItem(Item). Item manages relation.

cherouvim
Thank you cherouvim but You are suggesting bidirectional mapping that I already had tried (though tried again with exactly your configuration) and found same outcome that I wrote in my question "Bi-directional association is a pain because it leads to circularities in object graphs". I don't know how it responds to spring or other frameworks but my json serializer is quit unhappy with this circulation. Is it not possible to use unidirectional association (though not recommended by hibernate)?
Leslie Norman
Oh, OK. Your problem then is serializing the hibernate enhanced entities and not mapping with hibernate. I suggest sticking to the bidirectional mapping and trying to solve the json serialization issue.
cherouvim
I agree, bidirectional is a recommended way but only for the sake of curiosity, is it not possible to achieve the said purpose through unidirectional mapping?
Leslie Norman