异常: 分离的实体被传递给持久化

我已经成功地用 Hibernate 编写了我的第一个主子示例。几天后,我再次使用它,并升级了一些库。不知道我做了什么,但我再也不能让它运行了。有没有人能帮我找出代码中哪里出错了,并返回以下错误消息:

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 映射:

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

编辑: 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;
}
}

编辑: 从客户端发送的 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}]}

编辑: 一些细节:
我试图通过以下两种方法来保存发票:

  1. 上面提到的手工制作 Json 对象并将其传递给 Fresh 会话服务器。在这种情况下,绝对 在呼叫之前没有进行任何活动 保存方法,所以不应该有任何打开的会话 除了保存方法

  2. 中打开的那个
  3. 加载现有数据 GetInvoice 方法,它们传递相同的 数据删除键值。这也是我相信 在保存为 事务正在 getInvoice 方法中提交

在这两种情况下,我都收到了相同的错误消息,迫使我相信休眠配置文件或实体类或保存方法出了问题。

请让我知道,如果我应该提供更多的细节

245806 次浏览

很有可能问题出在您在这里给我们展示的代码之外。您正在尝试更新与当前会话无关的对象。如果它不是 Invoice,那么它可能是一个已经被持久化的 InvoiceItem,从数据库获取,在某种会话中保持活动,然后尝试在新的会话中持久化它。这不可能。作为一般规则,永远不要在会话之间保持持久化对象活动。

解决方案就是从您试图持久化它的同一个会话中获取整个对象图。在网络环境中,这意味着:

  • Obtain the session
  • 通过主键获取需要更新或添加关联到.Preferabley 的对象
  • 改变需要的东西
  • 保存/更新/驱逐/删除所需内容
  • Close/commit your session/transaction

如果您一直存在问题,请发布一些正在调用您的服务的代码。

You didn't provide many relevant details so I will guess that you called getInvoice and then you used result object to set some values and call save with assumption that your object changes will be saved.

但是,persist操作适用于全新的瞬态对象,如果已经分配了 id,它将失败。在您的情况下,您可能希望调用 saveOrUpdate而不是 persist

你可以在这里找到一些讨论和参考资料

这里您已经使用了本机并为主键赋值,本机主键是自动生成的。

因此问题就来了。

这存在于@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;

对于使用 EntityManager merge ()修复的 JPA 而言,可以使用 keep ()

EntityManager em = getEntityManager();
try {
em.getTransaction().begin();
em.merge(fieldValue);
em.getTransaction().commit();
} catch (Exception e) {
//do smthng
} finally {
em.close();
}

两种解决方案

  1. use merge if you want to update the object
  2. 如果只想保存新对象,可以使用 save (确保 id 为 null,以便让 hibernate 或数据库生成它)
  3. 如果您使用的映射类似于
@OneToOne(fetch = FetchType.EAGER,cascade=CascadeType.ALL)
@JoinColumn(name = "stock_id")

然后使用 CascadeType.ALLCascadeType.MERGE

我也有同样的问题,因为我在写作

@GeneratedValue(strategy = GenerationType.IDENTITY)

我删除了这一行,因为我不需要它的时候,我正在测试的对象等。我认为你的案子是 <generator class="native" />

我没有任何控制器,我的 API 也没有被访问,它只是用于测试(目前)。

分享我的经验作为问题的标题是更一般的。其中一个原因可能是@GeneratedValue 用于惟一的 id,但随后又设置了 id。

例子

@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "COURSE")
public class Course {
   

@Id
@GeneratedValue
private Long id;


private String name;


}

下面的代码抛出异常: org.hibernate.PersistentObjectException: detached entity passed to persist:a.b.c.Course因为构造函数中的 身份已设定

Course course = new Course(10L, "testcourse");
testEntityManager.persist(course);

解决方案

避免设置 Id,因为它是自动生成的(在实体类中指定)

Course course = new Course();
course.setName("testcourse");
testEntityManager.persist(course);

如果你用的是龙目岛的你就写

@ToString.Exclude

你在哪里写重新安置