我在通过播放框架持久保存数据时遇到问题。也许不可能达到这个结果,但是如果可行的话,那将是非常好的。
简单 :我有一个复杂的模型(带地址的商店),我想立即更改带地址的商店,并以相同的方式存储它们(shop.save())。但是detached entity passed to persist会发生错误。
detached entity passed to persist
宇date历史 05.11
05.11
mappedBy="shop"
09.11
找到一种解决方法,但这是通用的
16.11
更新示例html表单,感谢@Pavel
Dateil :我尝试将问题减少到最低程度: Model :
@Entity public class Shop extends Model { @Required(message = "Shopname is required") public String shopname; @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER, mappedBy="shop") public List<Address> addresses; } @Entity public class Address extends Model { @Required public String location; @ManyToOne public Shop shop; }
现在我的 Frontendcode :
#{extends 'main.html' /} #{form @save(shop?.id)} <input type="hidden" name="shop.id" value="${shop?.id}"/> #{field 'shop.shopname'} <label for="shopName">Shop name:</label> <input type="text" name="${field.name}" value="${shop?.shopname}" class="${field.errorClass}" /> #{/field} <legend>Addressen</legend> #{list items: shop.addresses, as: "address"} <input type="hidden" name="shop.addresses[${address_index - 1}].id" value="${address.id}"/> <label>Location</label> <input name="shop.addresses[${address_index - 1}].location" type="text" value="${address.location}"/> #{/list} <input type="submit" class="btn primary" value="Save changes" /> #{/form}
我只有商店本身的ID和商店名称,可以通过POST传递,例如: ?shop.shopname=foo
?shop.shopname=foo
有趣的是地址列表,在那里我有Id和地址中的位置,结果将是somthin,如:?shop.shopname=foo&shop.addresses[0].id=1&shop.addresses[0].location=bar。
?shop.shopname=foo&shop.addresses[0].id=1&shop.addresses[0].location=bar
现在, 控制器 部分用于数据:
public class Shops extends CRUD { public static void form(Long id) { if (id != null) { Shop shop = Shop.findById(id); render(shop); } render(); } public static void save(Long id, Shop shop) { // set owner manually (dont edit from FE) User user = User.find("byEmail", Security.connected()).first(); shop.owner = user; // Validate validation.valid(shop); if (validation.hasErrors()) render("@form", shop); shop.save(); index(); }
现在的 问题是 :当我更改地址数据时,代码到达shop.save();对象商店,里面充满了所有数据,并且一切看起来都很好,但是当hibernate尝试持久化数据时,detached entity passed to persist就会发生错误:(
shop.save();
我尝试更改获取模式,级联类型,并且还尝试了以下操作:
Shop shop1 = shop.merge(); shop1.save();
不幸的是,没有任何结果,或者发生了错误,或者没有地址数据将被存储。有没有办法以这种方式存储数据?
如果有不清楚的地方,请给我写信,我很乐意提供尽可能多的信息。
更新1 我也把问题放在谷歌用户组上
更新2 + 3 在用户组的帮助下(感谢bryan w。)和mericano1的回答,我在这里找到了通用的解决方法。
首先,您必须cascade=CascadeType.ALL从addressesshop.class的属性中删除。然后,您必须save在shops.class中更改方法。
cascade=CascadeType.ALL
addresses
save
public static void save(Long id, Shop shop) { // set owner manually (dont edit from FE) User user = User.find("byEmail", Security.connected()).first(); shop.owner = user; // store complex data within shop storeData(shop.addresses, "shop.addresses"); storeData(shop.links, "shop.links"); // Validate validation.valid(shop); if (validation.hasErrors()) render("@form", shop); shop.save(); index(); }
存储数据的通用方法如下所示:
private static <T extends Model> void storeData(List<T> list, String parameterName) { for(int i=0; i<list.size(); i++) { T relation = list.get(i); if (relation == null) continue; if (relation.id != null) { relation = (T)Model.Manager.factoryFor(relation.getClass()).findById(relation.id); StringBuffer buf = new StringBuffer(parameterName); buf.append('[').append(i).append(']'); Binder.bind(relation, buf.toString(), request.params.all()); } // try to set bidiritional relation (you need an interface or smth) //relation.shop = shop; relation.save(); } }
我在Shop.class中添加了 Links 列表,但是我不会更新其他代码段,因此如果发生编译错误,请注意。
当您在Hibernate中更新复杂实例时,您需要确保它来自数据库(首先获取它,然后更新同一实例),以避免出现“分离实例”问题。
通常,我通常总是先获取,然后仅更新期望从UI中获得的特定字段。
您可以使用以下代码使代码更通用
(T)Model.Manager.factoryFor(relation.getClass()).findById(relation.id);