org.hibernate.PersistentObjectException:分离的实体传递给持久化


89

我已经成功用冬眠写了我的第一个主要的孩子例子。几天后,我再次使用它并升级了一些库。不知道我做了什么,但是我再也无法运行了。有人可以帮助我找出返回以下错误消息的代码中的错误吗:

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

发票.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对象,并将其传递给服务器的新会话。在这种情况下,在调用save方法之前绝对没有进行任何活动,因此除了在save方法中打开的会话外,不应有任何打开的会话。

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

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

请让我知道是否需要提供更多详细信息

Answers:


119

您没有提供很多相关的详细信息,所以我猜您调用了它getInvoice,然后您使用result对象设置了一些值,save并假设您的对象更改将被保存进行调用。

但是,该persist操作适用于全新的临时对象,如果已经分配了ID,则它将失败。在您的情况下,您可能想致电saveOrUpdate而不是persist

您可以在此处找到有关JPA / EJB代码的一些讨论和参考资料“传递给持久错误的分离实体”


谢谢@Alex Gitelman。我在原始问题的底部添加了一些详细信息。有助于理解我的问题吗?或请让我知道其他详细信息会有所帮助。
WSK

7
您的参考帮助我找到了愚蠢的错误。我没有为子表中的主键发送“ itemId”的空值。因此,休眠状态假设对象在某个会话中已经存在。谢谢您的建议
WSK

现在,我收到此错误:“ org.hibernate.PropertyValueException:not-null属性引用的是空值或瞬态值:example.forms.InvoiceItem.invoice”。你能给我一些提示吗?在此先感谢
WSK

您必须使发票处于持久状态,而不是临时状态。这意味着必须已经为其分配ID。因此,Invoice请先保存,然后再获取ID并保存InvoiceItem。您也可以级联玩。
Alex Gitelman

13

在这里,您已经使用了本机并将值分配给主键,因为本机主键是自动生成的。

因此,问题来了。


1
如果您认为可以为已经得到公认的答案的问题提供其他信息,请提供更详尽的解释。
ChicagoRedSox 2014年

8

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

4

问题很可能出在您在此处显示给我们的代码之外。您正在尝试更新与当前会话不相关的对象。如果不是Invoice,则可能是已持久保存的InvoiceItem(从db获取),并在某种会话中保持活动状态,然后尝试将其持久保存在新的会话中。这是不可能的。通常,永远不要在会话之间使持久对象保持活动状态。

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

  • 取得会议
  • 获取您需要更新或添加关联的对象。主键优先
  • 更改所需的内容
  • 保存/更新/逐出/删除所需内容
  • 关闭/提交会话/事务

如果您仍然遇到问题,请发布一些调用服务的代码。


谢谢@joostschouten。显然,在调用保存方法之前应该没有打开的会话,正如我在原始问题底部添加的“更多详细信息”中提到的那样。在调用保存方法之前,有什么方法可以检查某些会话是否存在?
WSK

您的假设“显然,在调用保存方法之前不应打开会话”是错误的。在您的情况下,您要将事务保存在每个保存和获取周围,这意味着打开的会话不应该发生,如果它们没有用的话。您的问题似乎出在处理JSON的代码中。在这里,您传递带有已存在发票项目的发票(它们具有ID)。用null id传递它,它很可能会工作。或者让处理JSON的服务从db获取发票项,将其添加到发票并将其保存在从中获取发票的同一会话中。
joostschouten 2011年

@joostschouten现在,我收到此错误:“ org.hibernate.PropertyValueException:not-null属性引用了null或瞬时值:example.forms.InvoiceItem.invoice”。你能给我个主意吗?在此先感谢
WSK

1
对我来说,这听起来像是一个新问题。您尚未与我们共享重要的代码。处理JSON,生成模型对象并调用的代码将持久保存。此异常告诉您您要使用空发票保留invoiceItem。理所当然不能做到的。请发布实际构建模型对象的代码。
joostschouten 2011年

@joostschouten对我来说这很有意义,但是问题是我正在使用用于JSON的框架“ qooxdoo”并创建对服务器的RPC调用,在该服务器中,我从相同的框架安装了RPC服务器实用程序。因此,所有内容都包装在框架类中。提取并发布数千行可能不切实际。另一方面,我们可以在服务器端监视已创建的“ theInvoice”对象?或通过显示休眠调试/跟踪信息?
WSK

2

两种解决方案:1.如果要更新对象,请使用合并; 2.如果要仅保存新对象,请使用保存(请确保标识为null以让休眠或数据库生成它); 3.如果使用的是
@OneToOne( fetch = FetchType.EAGER,cascade = CascadeType.ALL)@JoinColumn(name =“ stock_id”)

然后使用CascadeType.ALL到CascadeType.MERGE

感谢Shahid Abbasi


0

对于使用EntityManager merge()而不是persist()修复的JPA

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

0

我有“相同”问题,因为我在写

@GeneratedValue(strategy = GenerationType.IDENTITY)

由于当前不需要该行,因此我删除了该行,我正在测试对象等。我认为是<generator class="native" />你的情况

我没有任何控制器,并且无法访问我的API,这仅用于测试(目前)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.