Cascading Deletion in the Gaming Frame - How to Model Entities

advertisements

While creating an online shop application using play-1.2.4 ,I ran into some problems with jpa..I wanted to provide an admin area using the CRUD module in play.Here ,an admin user can create/edit or delete the entities in the application(like Customers,Orders,Items etc).

A Customer can create Orders.Each Order will have a Set of CartItems.When an Order is deleted,the corresponding CartItems must be deleted.When a Customer is deleted,all his Orders must be deleted as well.I thought I could get this by setting the cascade property in jpa annotation.

I modelled it like this

Customer.java

@Entity
public class Customer extends Model {
    @Email
    @Required
    public String email;
    ...
    @OneToMany(mappedBy="customer", cascade=CascadeType.ALL)
    public List<Order> orders;
    @OneToOne
    public PayMethod currentPayment;
    ...
}

Order.java

@Entity
public class Order extends Model {
    @OneToMany( cascade=CascadeType.ALL,orphanRemoval=true,fetch=FetchType.EAGER)
    public Set<CartItem> cartItems;

    @ManyToOne
    public Customer customer;
    @ManyToOne
    public PayMethod paymentMethod;
    ...
}

CartItem.java

@Entity
public class CartItem extends Model implements Comparable<CartItem>{
    @ManyToOne
    public Item item;
    public int quantity;
}

PayMethod.java

@Entity
public class PayMethod extends Model {
    @Required
    public String cardNumber;
    @ManyToOne
    public Customer customer;
    ...
}

The following database tables were created

customer table

id |  email      |   fullname    | currentpayment_id
---|-------------|---------------|-----------------
2  |[email protected]| jon           |29

order table

 id |customer_id | paymentmethod_id
----+------------+-----------------
 25 |  2         |       29

cartitem table

id  | quantity | item_id
----+----------+---------
 26 |        1 |      14

*order_cartitem table*

 order_id | cartitems_id
----------+--------------
       25 |           26

In the Admin interface created using CRUD(I haven't implented any methods ,just using the provided CRUD module as is),I tried to delete a Customer,but then,I get this error,

ERROR: update or delete on table "cartitem" violates foreign key constraint "fk7ff437ad3e28aa91" on table "order_cartitem"
Detail: Key (id)=(26) is still referenced from table "order_cartitem".
08:03:03,031 ERROR ~ Could not synchronize database state with session

Is there something wrong with the way I modelled the Entities? I thought the delete on the Customer will be cascaded to Order and that in turn will cascade to its CartItems .

What do I have to do to get this cascading effect?Or do I have to manually remove each contained instances of CartItems?

EDIT: As per Seb's reply

class Order extends Model {
    @OneToMany(mappedBy="order", cascade=CascadeType.ALL,orphanRemoval=true,fetch=FetchType.EAGER)
    public Set<CartItem> cartItems;
    ...
}

class CartItem extends Model implements Comparable<CartItem>{

    @ManyToOne
    public Item item;

    public int quantity;

    @ManyToOne
    public Order order;
...
}

static void addItemToCart(Long itemId,Long orderId,String quantity) {
    Item item = Item.findById(itemId);
   Order order = Order.findById(orderId);
   int qty = Integer.parseInt(quantity);
   CartItem cartItem = new CartItem(item,qty);
   cartItem.order=order;
   order.addItem(cartItem, qty);
   order.save();
...
}

This gets rid of order_cartitem table and adds a field order_id to cartitem table

cartitem table

     id | quantity | item_id | order_id
    ----+----------+---------+----------
     26 |        1 |      14 |       25
     27 |        1 |      20 |       25

The admin(CRUD) interface,lists the Customers and Orders.When a particular Customer(the one who created the Order) is selected and the delete button is clicked,it results in a JPA error

JPA error
A JPA error occurred (Cannot commit): collection owner not associated with session: models.Order.cartItems

here is the stacktrace

If someone can understand why this is happening,please tell me.

Interestingly,I can click on the delete button for a particular Order,and it successfully calls the following controller method,

Admin.java

public static void deleteOrder(Long id) {
    Order order = Order.findById(id);
    order.delete();
    ...
}

which deletes the Order and all its CartItems successfully.. So,why does this not happen when a Customer is deleted?


Make your relation bidirectionnal by adding this to your cartitem class

@ManyToOne
public Order order;

and a mappedBy="order" in your cartItems @OneToMany

then hibernate will better handles your deletion. I am guessing that without this bidirectionnal link, hibernate tried to firstly set the column to null. You can also try to allow null values in your join table to see what happens if you don't want to enable the bidirectional relationship