org.hibernate.PersistentObjectException:传递给persist的分离实体

WSK*_*WSK 75 hibernate

我用hibernate成功编写了我的第一个master child示例.几天后我再次拿起它并升级了一些图书馆.不知道我做了什么,但我再也不能让它再次运行.有人会帮助我弄清楚在返回错误消息后返回的代码中出现了什么问题:

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

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

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

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

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

编辑:从客户端发送的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}]}
Run Code Online (Sandbox Code Playgroud)

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

  1. 手动编写上面提到的json对象并将其传递给服务器的新鲜会话.在这种情况下,在调用save方法之前绝对没有进行任何活动,因此除了在save方法中打开的会话之外,不应该有任何打开的会话

  2. 使用getInvoice方法加载现有数据,并在删除键值后传递相同的数据.这也是我认为应该在保存之前关闭会话,因为事务是在getInvoice方法中提交的.

在这两种情况下,我都得到相同的错误消息,迫使我认为hibernate配置文件或实体类或save方法有问题.

如果我应该提供更多细节,请告诉我

Ale*_*man 103

你没有提供很多相关的细节,所以我猜你已经调用了getInvoice然后你使用结果对象来设置一些值,save并假设你的对象更改将被保存.

但是,persist操作适用于全新的瞬态对象,如果已分配id,则操作失败.在你的情况下,你可能想打电话saveOrUpdate而不是persist.

您可以在此处找到一些讨论和参考,其中"使用JPA/EJB代码传递给持久性错误的分离实体"

  • 你的参考帮我找到了愚蠢的错误.我发送的"itemId"的空值不是子表中的主键.所以hibernate假设对象已经存在于某个会话中.谢谢你的建议 (7认同)

小智 18

这存在于@ManyToOne 关系中。我通过使用 CascadeType.MERGE 而不是 CascadeType.PERSIST 或 CascadeType.ALL 解决了这个问题。希望对你有帮助。

@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name="updated_by", referencedColumnName = "id")
private Admin admin;
Run Code Online (Sandbox Code Playgroud)

解决方案:

@ManyToOne(cascade = CascadeType.MERGE)
@JoinColumn(name="updated_by", referencedColumnName = "id")
private Admin admin;
Run Code Online (Sandbox Code Playgroud)


jaf*_*mlp 13

分享我的经验,因为问题标题更通用。原因之一可能是 @GenerateValue 用于唯一 id,但随后又重新设置了 id。

例子

@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "COURSE")
public class Course {
   
    @Id
    @GeneratedValue
    private Long id;

    private String name;

}
Run Code Online (Sandbox Code Playgroud)

以下代码抛出异常:org.hibernate.PersistentObjectException: detached entity passed to persist:a.b.c.Course因为Id 是在构造函数中设置的。

Course course = new Course(10L, "testcourse");
testEntityManager.persist(course);
Run Code Online (Sandbox Code Playgroud)

解决方案

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

Course course = new Course();
course.setName("testcourse");
testEntityManager.persist(course);
Run Code Online (Sandbox Code Playgroud)


小智 10

在这里,您使用了本机并为主键分配值,本机主键是自动生成的.

因此问题即将来临.