错误:传递给持久化的分离实体 - 尝试持久化复杂数据(Play-Framework)

Tob*_*now 10 java hibernate playframework

我有一个问题,通过播放框架持久化数据.也许不可能取得这样的结果,但如果它能起作用那将是非常好的.

简单:我有一个复杂的模型(Shop with Addresses),我想立即用地址更改商店并以相同的方式存储它们(shop.save()).但是错误detached entity passed to persist发生了.

Udate历史 05.11

  • 05.11

    • 使用属性更新Model Shop mappedBy="shop"
    • 更新谷歌用户组的链接
  • 09.11

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

    • 更新示例html表单,感谢@Pavel
    • 将解决方法(更新09.11)更新为通用方法,感谢@ mericano1
  • 21.11
    • 我放弃了试图寻找解决方案并等待播放2.0 ...

Dateil:我尝试将问题减少到最低限度:
型号:

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

}
Run Code Online (Sandbox Code Playgroud)


@Entity
public class Address extends Model {

    @Required
    public String location;

    @ManyToOne
    public Shop shop;
}
Run Code Online (Sandbox Code Playgroud)

现在我的前端代码:

#{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}
Run Code Online (Sandbox Code Playgroud)

我只有商店本身的ID和通过POST提供的商店名称: ?shop.shopname=foo

interssting部分是地址列表,我在那里有Id和地址的位置,结果将像somthin一样:?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();
}
Run Code Online (Sandbox Code Playgroud)

现在问题:当我更改地址数据代码到达shop.save();对象商店充满了所有数据,一切看起来很好,但当hibernate试图持久化数据时,出现错误detached entity passed to persist:(

我试图改变获取模式,cascadetype,我也试过:

Shop shop1 = shop.merge();
shop1.save();
Run Code Online (Sandbox Code Playgroud)

不幸的是,没有任何工作,或者发生错误,或者不存储地址数据.有没有办法以这种方式存储数据?

如果有什么不清楚请写信给我,我很乐意尽可能多地提供信息.

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

更新2 + 3 在用户组的帮助下(感谢bryan w.)和mericano1的答案,我找到了一个通用的解决方法.

首先,您必须cascade=CascadeType.ALLaddressesshop.class中的属性中删除.然后你必须save在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();
}
Run Code Online (Sandbox Code Playgroud)

存储数据的通用方法如下所示:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

我在Shop.class中添加了一个链接列表,但我不会更新其他代码片段,因此如果发生编译错误则会收到警告.

mer*_*no1 6

当您在Hibernate中更新复杂实例时,您需要确保它来自数据库(首先获取它,然后更新同一个实例)以避免这个"分离实例"问题.

我通常更喜欢始终先获取,然后只更新我期望从UI中获取的特定字段.

您可以使用更加通用的代码

(T)Model.Manager.factoryFor(relation.getClass()).findById(relation.id);
Run Code Online (Sandbox Code Playgroud)