错误:传递给persist的分离实体 - 尝试保留复杂数据(Play-Framework) [英] Error: detached entity passed to persist - try to persist complex data (Play-Framework)
问题描述
我通过play-framework持续数据存在问题。也许它不可能实现这个结果,但如果它能够工作,它会非常好。
简单:我有一个复杂的模型(Shop with Addresses),我想一次性更改商店并将它们存储在同样的方式(shop.save())。但是发生错误分离的实体传递给persist
。
更新历史 05.11 09.11 16.11 Dateil : 现在我的前端代码: 我只有店铺本身的Id和店名通过POST提供,如: interssting部分是地址列表,我有地址和地址,结果就像是: 现在数据的 Controller 部分: 现在问题:当我更改地址数据的代码达到了 我试图改变fetch模式,cascadetype,我也尝试过: 不幸的是,没有任何工作,要么发生错误,要么没有地址数据将被存储。 更新1 更新2 + 3 首先,您必须从属性 存储数据的通用方法如下所示:
05.11
的模型商店mappedBy =shop
我尝试将问题降至最低:
模型:
@Entity
公共类商店扩展模型{
@Required(message =Shopname is required)
public String shopname;
@OneToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER,mappedBy =shop)
public List< Address>地址;
}
@Entity
public class Address extends Model {
@Required
public String location;
@ManyToOne
公共商店;
}
#{extends'main.html'/}
#{form @save(shop?.id)}
< input type =hiddenname =shop.idvalue =$ {shop?.id}/>
#{field'shop.shopname'}
< label for =shopName>商店名称:< / label>
#{/ field}
< legend> Addressen< / legend>
#{list items:shop.addresses,as:address}
< input type =hiddenname =shop.addresses [$ {address_index - 1}] .idvalue = $ {address.id}/>
< label>位置< / label>
< input name =shop.addresses [$ {address_index - 1}]。locationtype =textvalue =$ {address.location}/>
#{/ list}
< input type =submitclass =btn primaryvalue =保存更改/>
#{/ form}
?shop.shopname = foo
?shop.shopname = foo& shop.addresses [0] .id = 1& shop.addresses [0] .location =栏
。
$ b
公共类商店扩展CRUD {
公共商店= Shop.findById(ID);
render(shop);
}
render();
public static void save(Long id,Shop shop){
//手动设置所有者(不要从FE编辑)
用户用户= User.find(byEmail,Security.connected())。first();
shop.owner = user;
//验证
validation.valid(shop);
if(validation.hasErrors())
render(@ form,shop);
shop.save();
index();
}
shop.save();
对象商店里充满了所有数据,一切看起来都很好,但是当hibernate尝试持久化数据时,错误传递给persist的分离实体
发生:(
Shop shop1 = shop.merge();
shop1.save();
$ c
$ b
有没有办法存储数据如果有什么不清楚的地方请写信给我,我会很乐意尽可能地提供更多的信息。
我也将问题放在谷歌用户组合
在用户组的帮助下(感谢bryan w。)和一个来自mericano1的答案我在这里找到了一个通用的解决方法。
地址$>中删除
cascade = CascadeType.ALL
c $ c>在shop.class中。然后,您必须在shops.class中更改方法 save
。
public static void save(Long id,Shop shop){
//手动设置所有者不要从FE编辑)
User user = User.find(byEmail,Security.connected())。first();
shop.owner = user;
//在店内存储复杂数据
storeData(shop.addresses,shop.addresses);
storeData(shop.links,shop.links);
//验证
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());
}
//尝试设置bidiritional关系(您需要一个界面或不适用)
//relation.shop = shop;
relation.save();
$ / code $ / pre
我在Shop.class中添加了一个链接,但我不会更新其他代码片段,因此如果发生编译错误时会发出警告。
当您在Hibernate中更新一个复杂的实例时,您需要确保它来自数据库(先取得它,然后更新相同的实例),以避免这种'分离的实例'问题。
我通常更喜欢总是首先获取,然后只更新我期望从UI获得的特定字段。
您可以使用
( T)Model.Manager.factoryFor(relation.getClass())findById(relation.id)。
I have a problem with persisting data via play-framework. Maybe it's not possible to achive that result, but it would be really nice if it would work.
Simple: I have a complex Model (Shop with Addresses) and I want to change the shop with addresses at once and store them in the same way (shop.save()). But the error detached entity passed to persist
occurs.
Udate History 05.11
05.11
- update Model Shop with attribute
mappedBy="shop"
- update link to google user group
- update Model Shop with attribute
09.11
- find a workaround, but it's ot generic
16.11
- update example html form, thanks to @Pavel
- update workaround (update 09.11) to a generic method, thanks to @mericano1
- 21.11
- I gave up trying to find a solution and waiting for play 2.0...
Dateil:
I try to cut down the problem to a minimum:
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;
}
now my 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}
I have just the Id from the Shop itself and the shopname to deliver via POST like: ?shop.shopname=foo
The interssting part is the list of addresses and there I have the Id and the location from the address and the result would be somthin like: ?shop.shopname=foo&shop.addresses[0].id=1&shop.addresses[0].location=bar
.
Now the Controller part for the data:
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();
}
Now the problem: When i change the address data the code reaches the shop.save();
the object shop is filled with all data and everything looks fine, but when hibernate tryes to persist the data, the error detached entity passed to persist
occurs :(
I tried to change the fetch mode, the cascadetype and i also tried:
Shop shop1 = shop.merge();
shop1.save();
Unfortunately nothing worked, either the error occurs, or no address data will be stored. Is there a way to store the data in that way?
If there is somthing not clear please write to me, I would be glad to give as much information as possible.
Update 1 I also put the problem on the google user group
Update 2 + 3 With the help of the user group (thanks to bryan w.) and an Answer from mericano1 here I found a generic workaround.
First you have to remove cascade=CascadeType.ALL
from attribute addresses
in shop.class. Then you have to change the method save
within shops.class.
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();
}
the generic method to store the data looks like that:
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();
}
}
I added in Shop.class a list of Links, but I won't update the other code snippets, so be warned if compiling errors occur.
When you update a complex instance in Hibernate you need to make sure it is coming from the database (fetch it first, then update that same instance) to avoid this 'detached instance' problem.
I generally prefer to always fetch first and then only update specific fields I'm expecting from the UI.
You can make your code a bit more generic using
(T)Model.Manager.factoryFor(relation.getClass()).findById(relation.id);
这篇关于错误:传递给persist的分离实体 - 尝试保留复杂数据(Play-Framework)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!