【问题标题】:JPA: pattern for handling OptimisticLockExceptionJPA:处理 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();
    }
}

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

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

【问题讨论】:

    标签: java database jpa


    【解决方案1】:

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

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

    【讨论】:

    • 谢谢,这就是我最终实现的。就我而言,第二次尝试成功的机会很大。话虽如此,我承认按照 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 会更友好。(当然,你会继续使用适当的响应代码!这样的困境)

    【讨论】:

    • 如果我没有锁定,我会从数据库中得到死锁异常。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-28
    • 1970-01-01
    • 2017-01-07
    • 2012-01-21
    • 1970-01-01
    • 2015-05-28
    相关资源
    最近更新 更多