У меня проблема с постоянными данными через play-framework. Возможно, это невозможно достичь этого результата, но было бы очень приятно, если бы это сработало.
Простой. У меня есть сложная модель (магазин с адресами), и я хочу сразу изменить магазин с адресами и хранить их одинаково (shop.save()). Но возникает ошибка detached entity passed to persist
.
История Udate 05.11
-
05,11
- обновить Model Shop с атрибутом
mappedBy="shop"
- Обновить ссылку на группу пользователей google
- обновить Model 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;
}
@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&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
возникает:(
Я попытался изменить режим выборки, тип cascadetype, и я также попытался:
Shop shop1 = shop.merge();
shop1.save();
К сожалению, ничего не получилось, произошла ошибка или данные адреса не будут сохранены. Есть ли способ хранить данные таким образом?
Если есть что-то непонятное, напишите мне, я был бы рад предоставить как можно больше информации.
Обновление 1 Я также поставил проблему в группе пользователей google
Обновление 2 + 3 С помощью группы пользователей (спасибо bryan w.) И ответа от mericano1 здесь я нашел общий обходной путь.
Сначала вам нужно удалить cascade=CascadeType.ALL
из атрибута addresses
в shop.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();
}
общий метод хранения данных выглядит следующим образом:
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 список Ссылки, но я не буду обновлять другие фрагменты кода, поэтому будьте предупреждены, если возникнут компиляция ошибок.