在play框架中级联删除 - 如何为实体建模

时间:2012-03-05 04:34:39

标签: java jpa playframework cascade

使用play-1.2.4创建在线商店应用程序时,我遇到了jpa的一些问题。我想在play中使用CRUD module提供管理区域。这里,管理员用户可以创建/编辑或删除应用程序中的实体(如Customer s,Order s,Item等)。

Customer可以创建订单。每个Order都会有一组CartItem s。当Order被删除时,相应的CartItem必须当删除Customer时,他的所有订单也必须删除。我想我可以通过在jpa注释中设置cascade属性来实现这一点。

我像这样建模了

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;
    ...
}

创建了以下数据库表

客户表

id |  email      |   fullname    | currentpayment_id
---|-------------|---------------|-----------------
2  |jon@gmail.com| jon           |29 

订购表

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

购物台表

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

* order_cartitem表*

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

在使用CRUD创建的Admin界面中(我没有实现任何方法,只是按原样使用提供的CRUD模块),我试图删除一个Customer,但是,我得到了这个错误,

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

我对实体进行建模的方式有问题吗?我认为Customer上的删除会级联到Order,然后会级联到CartItem

我需要做些什么来获得这种级联效果?或者我是否必须手动删除每个包含CartItem s的实例?

编辑:根据Seb的回复

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();
...
}

这摆脱了 order_cartitem 表,并将字段 order_id 添加到 cartitem表

购物台表

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

admin(CRUD)界面,列出了CustomerOrder s。当选择了Customer(创建Order的人)时,单击删除按钮,结果为JPA error

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

这是stacktrace

如果有人能理解为什么会这样,请告诉我。

有趣的是,我可以点击特定delete button的{​​{1}},并成功调用以下控制器方法,

Admin.java

Order

成功删除public static void deleteOrder(Long id) { Order order = Order.findById(id); order.delete(); ... } 及其所有Order个.. 那么,为什么在删除CartItem时不会发生这种情况?

3 个答案:

答案 0 :(得分:2)

通过将此关系添加到您的cartitem类

,使您的关系双向化
@ManyToOne
public Order order;

和mappedBy =&#34; order&#34;在你的cartItems @OneToMany

然后hibernate会更好地处理你的删除。我猜测,没有这个双向链接,hibernate试图首先将列设置为null。您还可以尝试在联接表中允许空值,以查看如果您不想启用双向关系会发生什么

答案 1 :(得分:0)

JPA的级联属性存在问题。尝试使用来自hibernate的相关级联注释,并删除从JPA中使用的所有级联。以下是PayMethod模型类的示例:

// Be careful, import the realted annotations from hibernate (not JPA)
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;

@Entity
public class PayMethod extends Model {
    @Required
    public String cardNumber;

    @ManyToOne
    @OnDelete(action = OnDeleteAction.CASCADE)
    public Customer customer;
    ...
}

上面的方法适用于我的单向映射。我希望这能解决你的问题。

答案 2 :(得分:0)

我有类似的问题,但我没有使用List,我使用的是实体数组。

我的包装实体的问题是它没有像我通常对我的列表那样初始化数组(new MyEntity [0])(新的ArrayList(0))。

我意识到(经验方法)当尝试将“merge”调用到由“all-delete-orphan”关系关联的“null”数组时,“org.hibernate.AssertionFailure:集合所有者与会话无关: “起来了。

所以我的建议是:当使用“all-delete-orphans”关系时,用空实例初始化你的集合(包括数组)。