即使抛出错误,JPA EntityManager persist() 也会导致对象看起来分离

Posted

技术标签:

【中文标题】即使抛出错误,JPA EntityManager persist() 也会导致对象看起来分离【英文标题】:JPA EntityManager persist() causing object to appear detached even though an error was thrown 【发布时间】:2012-03-15 02:30:44 【问题描述】:

您好,我有一个带有以下功能的简单 DAO。

    public element createElement(Element e)

    em.persist(e);
    em.flush();

    return e;

Entity 表对 (type,value) 有唯一约束,我在下面进行了测试:

    public void testCreateElement() throws DataAccessException 
        // Start with empty Element table

        Element e = new Element();
        e.setType(myType.OTHER);
        e.setValue("1");
        dao.createElement(e);

        e = new Element();
        e.setType(MyType.OTHER);
        e.setValue("1");
        try
                // this should violate unique constraint of database.
                dao.createElement(e);
         catch (Exception ex) 
            System.out.println(ex);
        

        e.setValue("2");
        try
            // I expect this to work as there is no element with these values.
            dao.createElement(e);
         catch (Exception ex) 
            System.out.println(ex);
        
    

我的第一个捕获的错误发生在我的预期中,因为我知道我违反了约束,就我而言,第二个 try/catch 不应该抛出错误,但它确实发生了,我得到了什么:

javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: com.mypackage.Element

所以似乎在“e”上调用persist(),即使它没有被持久化已经导致hibernate认为它是一个分离的实体。

这很烦人,因为这些函数被处理 ConstraintViolation 异常的 JSF 前端使用,但故意保留对象,以便用户可以更改其中一个字段并重试,然后他们得到分离实体错误。

这种行为是否是休眠错误,因为我认为它真的不应该这样做?有没有办法在 DAO 级别解决这个问题,这样持久化就不会;如果我的对象实际上没有持久化,就不会将它视为分离的?

问候,

格伦x

【问题讨论】:

开玩笑,如果你创建一个新元素并在你调用的最后一个 createElement 之前设置正确的值会发生什么? 这有帮助吗?如果没有,这可能会起作用... e.setValue(1); e = dao.createElement(e); ( 不,这没有帮助。您的代码正是我所做的,上面的代码是故意编写来演示我的问题的,它代表一个使用 Web 前端创建元素的用户。我在原帖中也说了这么多。 【参考方案1】:

Hibernate 抛出的异常是不可恢复的。发生此类异常时,您唯一应该做的就是回滚事务并关闭会话。此类异常后会话(及其实体)的状态是不稳定的。

如果您想保持元素的副本不受影响,请使用merge() 而不是persist(),或者在持久化之前克隆元素。

请注意,异常是预期的,因为当 Hibernate 持久化并刷新实体时,它首先生成一个 ID 并将 ID 分配给实体,然后插入行,这会导致异常。因此,在异常之后,实体被分配了一个 ID,因此被 Hibernate 视为一个分离的实体。您可以尝试将 ID 重置为 null 并查看它是否有效,但我更喜欢在之前克隆实体或使用合并。

【讨论】:

以上是关于即使抛出错误,JPA EntityManager persist() 也会导致对象看起来分离的主要内容,如果未能解决你的问题,请参考以下文章

JPA:EntityManager 的 get-use-close 和延迟加载

如何设置 JPA EntityManager 查询的超时时间

在 Mysql 上使用 EntityManager JPA 运行脚本

何时将 EntityManager.find() 与 EntityManager.getReference() 与 JPA 一起使用

JPA 之 Hibernate EntityManager 使用指南

JPA EntityManager 返回没有 entityManager.clear 的陈旧值