Org.hibernate.PersistentObjectException: отдельный объект, переданный для сохранения
Я успешно написал первый пример основного ребенка с спящим режимом. Через несколько дней я снова взял его и обновил некоторые библиотеки. Не уверен, что я сделал, но я никогда не смог бы снова запустить его. Кто-нибудь поможет мне выяснить, что не так в коде, который возвращается после сообщения об ошибке:
org.hibernate.PersistentObjectException: detached entity passed to persist: example.forms.InvoiceItem
at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:127)
at org.hibernate.impl.SessionImpl.firePersist(SessionImpl.java:799)
at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:791)
.... (truncated)
hibernate mapping:
<hibernate-mapping package="example.forms">
<class name="Invoice" table="Invoices">
<id name="id" type="long">
<generator class="native" />
</id>
<property name="invDate" type="timestamp" />
<property name="customerId" type="int" />
<set cascade="all" inverse="true" lazy="true" name="items" order-by="id">
<key column="invoiceId" />
<one-to-many class="InvoiceItem" />
</set>
</class>
<class name="InvoiceItem" table="InvoiceItems">
<id column="id" name="itemId" type="long">
<generator class="native" />
</id>
<property name="productId" type="long" />
<property name="packname" type="string" />
<property name="quantity" type="int" />
<property name="price" type="double" />
<many-to-one class="example.forms.Invoice" column="invoiceId" name="invoice" not-null="true" />
</class>
</hibernate-mapping>
EDIT: InvoiceManager.java
class InvoiceManager {
public Long save(Invoice theInvoice) throws RemoteException {
Session session = HbmUtils.getSessionFactory().getCurrentSession();
Transaction tx = null;
Long id = null;
try {
tx = session.beginTransaction();
session.persist(theInvoice);
tx.commit();
id = theInvoice.getId();
} catch (RuntimeException e) {
if (tx != null)
tx.rollback();
e.printStackTrace();
throw new RemoteException("Invoice could not be saved");
} finally {
if (session.isOpen())
session.close();
}
return id;
}
public Invoice getInvoice(Long cid) throws RemoteException {
Session session = HbmUtils.getSessionFactory().getCurrentSession();
Transaction tx = null;
Invoice theInvoice = null;
try {
tx = session.beginTransaction();
Query q = session
.createQuery(
"from Invoice as invoice " +
"left join fetch invoice.items as invoiceItems " +
"where invoice.id = :id ")
.setReadOnly(true);
q.setParameter("id", cid);
theInvoice = (Invoice) q.uniqueResult();
tx.commit();
} catch (RuntimeException e) {
tx.rollback();
} finally {
if (session.isOpen())
session.close();
}
return theInvoice;
}
}
Invoice.java
public class Invoice implements java.io.Serializable {
private Long id;
private Date invDate;
private int customerId;
private Set<InvoiceItem> items;
public Long getId() {
return id;
}
public Date getInvDate() {
return invDate;
}
public int getCustomerId() {
return customerId;
}
public Set<InvoiceItem> getItems() {
return items;
}
void setId(Long id) {
this.id = id;
}
void setInvDate(Date invDate) {
this.invDate = invDate;
}
void setCustomerId(int customerId) {
this.customerId = customerId;
}
void setItems(Set<InvoiceItem> items) {
this.items = items;
}
}
InvoiceItem.java
public class InvoiceItem implements java.io.Serializable {
private Long itemId;
private long productId;
private String packname;
private int quantity;
private double price;
private Invoice invoice;
public Long getItemId() {
return itemId;
}
public long getProductId() {
return productId;
}
public String getPackname() {
return packname;
}
public int getQuantity() {
return quantity;
}
public double getPrice() {
return price;
}
public Invoice getInvoice() {
return invoice;
}
void setItemId(Long itemId) {
this.itemId = itemId;
}
void setProductId(long productId) {
this.productId = productId;
}
void setPackname(String packname) {
this.packname = packname;
}
void setQuantity(int quantity) {
this.quantity = quantity;
}
void setPrice(double price) {
this.price = price;
}
void setInvoice(Invoice invoice) {
this.invoice = invoice;
}
}
EDIT: Объект JSON, отправленный с клиента:
{"id":null,"customerId":3,"invDate":"2005-06-07T04:00:00.000Z","items":[
{"itemId":1,"productId":1,"quantity":10,"price":100},
{"itemId":2,"productId":2,"quantity":20,"price":200},
{"itemId":3,"productId":3,"quantity":30,"price":300}]}
РЕДАКТИРОВАТЬ: Некоторые детали:
Я попытался сохранить счет-фактуру, выполнив следующие два способа:
-
Вручную изготовлено выше
json и передал его на свежий
сеанс сервера. В этом случае абсолютно
до вызова не предпринималось никаких действий
сохранить метод, поэтому не должно быть открытого сеанса
кроме того, который открыт в режиме сохранения
-
Загрузите существующие данные, используя
Метод getInvoice, и они прошли одинаковые
данные после удаления значения ключа. Это тоже я считаю
должен закрыть сеанс перед сохранением
транзакция совершается в методе getInvoice.
В обоих случаях я получаю такое же сообщение об ошибке, которое заставляет меня полагать, что что-то не так, либо с конфигурационным файлом спящего режима или классами сущностей, либо с сохранением метода.
Пожалуйста, дайте мне знать, если я должен предоставить более подробную информацию
Ответы
Ответ 1
Вы не представили много релевантных деталей, поэтому я предполагаю, что вы вызвали getInvoice
, а затем вы использовали объект результата для установки некоторых значений и вызывали save
с предположением, что ваши изменения объекта будут сохранены.
Тем не менее, операция persist
предназначена для новых объектов переходного процесса, и она терпит неудачу, если идентификатор уже назначен. В вашем случае вы, вероятно, хотите вызвать saveOrUpdate
вместо persist
.
Вы можете найти некоторые обсуждения и ссылки здесь > " удаленный объект, переданный для сохранения ошибки " с кодом JPA/EJB
Ответ 2
Здесь вы используете native и присваиваете значение первичному ключу, в собственном первичном ключе автоматически генерируется.
Следовательно, проблема идет.
Ответ 3
Скорее всего, проблема лежит вне кода, который вы здесь показываете. Вы пытаетесь обновить объект, который не связан с текущим сеансом. Если это не счет-фактура, возможно, это InvoiceItem, который уже был сохранен, получен из db, сохранен в каком-то сеансе, а затем вы пытаетесь сохранить его на новом сеансе. Это невозможно. Как правило, никогда не сохраняйте сохраненные объекты в сеансах.
Решение будет, т.е. получать весь граф объекта из того же сеанса, с которым вы пытаетесь его сохранить. В веб-среде это означало бы:
- Получить сеанс
- Извлеките объекты, необходимые для обновления или добавления ассоциаций. Предпочтительно по их первичному ключу.
- Изменить то, что нужно
- Сохранить/обновить/выселить/удалить то, что вы хотите.
- Закрыть/зафиксировать сеанс/транзакцию
Если у вас возникли проблемы, отправьте код, вызывающий вашу службу.
Ответ 4
Это существует в отношении @ManyToOne. Я решил эту проблему, просто используя CascadeType.MERGE вместо CascadeType.PERSIST или CascadeType.ALL. Надеюсь, это поможет вам.
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name="updated_by", referencedColumnName = "id")
private Admin admin;
Решение:
@ManyToOne(cascade = CascadeType.MERGE)
@JoinColumn(name="updated_by", referencedColumnName = "id")
private Admin admin;
Ответ 5
Согласовано с ответом от @peterh Возможно, вы установили первичный ключ вложенного ссылочного класса, который имеет первичный ключ типа auto/identity
Ответ 6
Для JPA исправлено использование EntityManager merge() вместо persist()
EntityManager em = getEntityManager();
try {
em.getTransaction().begin();
em.merge(fieldValue);
em.getTransaction().commit();
} catch (Exception e) {
//do smthng
} finally {
em.close();
}