错误:传递给persist的分离实体 - 尝试保留复杂数据(Play-Framework) [英] Error: detached entity passed to persist - try to persist complex data (Play-Framework)

查看:114
本文介绍了错误:传递给persist的分离实体 - 尝试保留复杂数据(Play-Framework)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我通过play-framework持续数据存在问题。也许它不可能实现这个结果,但如果它能够工作,它会非常好。



简单:我有一个复杂的模型(Shop with Addresses),我想一次性更改商店并将它们存储在同样的方式(shop.save())。但是发生错误分离的实体传递给persist

更新历史
05.11



  • 05.11




    • 更新具有属性的模型商店mappedBy =shop

    • 更新指向google用户组的链接


  • 09.11




    • 找到一个解决方法,但它不是通用的


  • 16.11


    • 更新示例html表单,感谢@Pavel

    • 更新解决方法(更新09.11)一个通用的方法,感谢@ mericano1


  • 21.11


    • 我放弃了试图找到一个解决方案,并等待玩2.0 ...




Dateil
我尝试将问题降至最低:


模型

  @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}

我只有店铺本身的Id和店名通过POST提供,如:?shop.shopname = foo



interssting部分是地址列表,我有地址和地址,结果就像是:?shop.shopname = foo& shop.addresses [0] .id = 1& shop.addresses [0] .location =栏


$ b

现在数据的 Controller 部分:

 公共类商店扩展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的分离实体发生:(



我试图改变fetch模式,cascadetype,我也尝试过:

  Shop shop1 = shop.merge(); 
shop1.save();

$ b

不幸的是,没有任何工作,要么发生错误,要么没有地址数据将被存储。
有没有办法存储数据如果有什么不清楚的地方请写信给我,我会很乐意尽可能地提供更多的信息。



更新1
我也将问题放在谷歌用户组合



更新2 + 3
在用户组的帮助下(感谢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 persistoccurs.

Udate History 05.11

  • 05.11

    • update Model Shop with attribute mappedBy="shop"
    • update link to google user group
  • 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆