JPA:处理 OptimisticLockException 的模式

Posted

技术标签:

【中文标题】JPA:处理 OptimisticLockException 的模式【英文标题】:JPA: pattern for handling OptimisticLockException 【发布时间】:2011-09-26 08:45:11 【问题描述】:

在 (REST) Web 服务中处理 OLE 的正确模式是什么?这就是我现在正在做的,例如,

protected void doDelete(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException 

    ...
    ...
    ...

    try 
        try 
            em.getTransaction().begin();
            // ... remove the entity
            em.getTransaction().commit();
         catch (RollbackException e) 
            if (e.getCause() instanceof OptimisticLockException) 
                try 
                    CLog.e("optimistic lock exception, waiting to retry ...");
                    Thread.sleep(1000);
                 catch (InterruptedException ex) 
                
                doDelete(request, response);
                return;
            
        

        // ... write response

     catch (NoResultException e) 
        response.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
        return;
     finally 
        em.close();
    

每当您在代码中看到休眠时,很有可能它是不正确的。有没有更好的方法来处理这个问题?

另一种方法是立即将故障发送回客户端,但我不想让他们担心。正确的做法似乎是尽一切努力使请求在服务器上成功,即使需要一段时间。

【问题讨论】:

【参考方案1】:

如果您收到乐观锁定异常,则表示其他事务已提交 更改您尝试更新/删除的实体。由于其他事务已提交,因此立即重试可能会有很大的成功机会。

我也会让方法在 N 次尝试后失败,而不是等待 ***Exception 发生。

【讨论】:

谢谢,这就是我最终实现的。就我而言,第二次尝试成功的机会很大。话虽如此,我承认按照 Augusto 的建议,正确的做法是让客户重试。 获得 OLE 的另一个原因是实体 version 与数据库中的实体不同。它发生在更新操作和一段时间后重试无济于事。另请注意,您要更新的实体必须具有版本的 getter 和 setter 方法。这听起来合乎逻辑,但例如,我正在做某种类型的通用存储库来处理基本的 CRUD 操作,而 JPA 无法访问版本的设置器,在下次更新时提供 OLE。 只是“立即重试”根本不是一个好主意。发生此异常是因为 2 个用户同时编辑同一条信息。一个会成功,而另一个会简单地覆盖第一个所做的。乐观锁定将通过在第二次提交时抛出异常来避免这种情况。这需要用户首先进行干预,以确保两个版本的输入得到验证并以一种或另一种方式合并。 自动重试OptimisticLockException 上的操作不是一个好主意,因为这样可能会丢失重要的更新。【参考方案2】:

“政治上正确”的答案是返回一个 HTTP 409(冲突),这与乐观锁定的想法完美匹配。您的客户应该管理它,可能在几秒钟后重试。

我不会在您的应用中添加重试逻辑,因为当您返回 40X 代码时,您的客户端已经处理了各种情况。

【讨论】:

如果你在一个独立的应用程序中,这不是一个有用的回报。仅当您碰巧处于面向 Web 的环境中时,它才有意义。【参考方案3】:

顺便说一句,catch (InterruptedException e) 总是一个坏主意,因为系统已要求您取消计算,而您忽略了它。在 Web 服务的上下文中,InterruptedException 将是向客户端发出错误信号的另一个很好的理由。

【讨论】:

实际上,无论他是否记录异常,系统都在强制代码停止。异常被捕获后,执行不会返回到try块。 虽然这是真的,但它并不能回答问题。我还想补充一点,捕捉异常很好。但是,您应该调用Thread.currentThread.interrupt(),因为线程的中断状态已被异常清除。【参考方案4】:

如果您只是要继续重试直到它正常工作,为什么不禁用乐观锁定?您应该让来电者知道他们是根据过时的信息做出的决定!如果您控制双方,则可以返回适当的 400 代码。如果它是公开的,那么对任意客户端只返回 500 会更友好。(当然,你会继续使用适当的响应代码!这样的困境)

【讨论】:

如果我没有锁定,我会从数据库中得到死锁异常。

以上是关于JPA:处理 OptimisticLockException 的模式的主要内容,如果未能解决你的问题,请参考以下文章

处理 JPA 规范和 spring-data-jpa 时如何使用声明 Stream 作为返回类型

Spring Boot Jpa 批处理-CannotCreateTransactionException

处理 NULL 参数值的 JPA 查询

一个创建函数可以使用 EntityManager (JPA) 处理任何类型的对象吗?

处理 JPA 中的空或 null 集合参数

为啥 JPA 默认使用 FetchType EAGER 来处理 @ManyToOne 关系