当前位置: 首页 > 知识库问答 >
问题:

如何用悲观锁定处理Hibernate中的“StaleObjectStateException”?

万俟超
2023-03-14

编辑:在这种情况下,因为我使用了“版本”注释,所以我使用了乐观锁定,而不是悲观锁定。< br >如果我删除版本并因此禁用乐观锁定。悲观锁定会接管,性能会显著下降。< br >所以我想我必须忍受乐观锁定和偶尔的异常。有没有更好的解决办法?

原文:我目前通过ajp在apache 2.2负载均衡器中有多个Tomcat实例。后端系统处于Hibernate状态。该系统为多个用户和请求提供服务,对于请求,它会从用户帐户中扣除一个信用额度。

我使用hibernate的悲观锁定来管理信用扣减
我在日志中不断收到关于用户帐户对象的以下信息:

org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)

法典

private boolean decUserAccountQuota(UserAccount userAccount, int creditDec) {

        if(userAccount.getBalance() <1) return false;
        String userName = userAccount.getUsername();
        Manager manager = getManager(userName);


        try{
            manager.beginTransaction();
            manager.refresh(userAccount, LockMode.UPGRADE); //this uses pessimistic locking, this calls sessionFactory.getCurrentSession().refresh();   
            userAccount.setBalance(userAccount.getBalance()-creditDec);
            manager.commitTransaction(); //this causes the Exception
        }catch(Exception exp){
            exp.printStackTrace();
            manager.rollbackTransaction();
            return false;
        }finally{
            manager.closeSession();
        }
        return true;
    }

问题:

  1. 如何防止发生此异常。这里发生的事情是多个线程尝试更新相同的实体,一个线程成功,因此,当下一个线程提交数据时,它看到它已经被修改并最终抛出 StaleObjectStateException。但是,如果我已经使用了悲观锁定,那么异常怎么还会发生呢?
  2. 在管理用户帐户信用系统的性能和完整性方面,是否有更好的方法?

共有2个答案

颜奇希
2023-03-14

在您的代码中,下面的行没有采用悲观锁,尽管您指定了它,因为您的数据库不支持 SELECT。对于更新 .

manager.refresh(userAccount, LockMode.UPGRADE); 

由于这个原因,hibernate使用一种称为LockMode的替代锁定模式。读取userAccount,它是基于实体中@Version属性的乐观锁定。

我搜索了是否有任何其他方法可以强制Hibernate对您的场景采取悲观锁定,但看起来它是不可能的。

回到您关于如何最小化或避免StaleObjectStateException的问题,我的想法如下:-

对用户帐户对象进行同步。虽然这似乎会影响性能,但只有当同一用户发出太多并发请求时才会发生这种情况。此外,牺牲一点性能来确保用户不需要抛出异常并重试将是理想的场景。

如果您找到了其他解决方案,请与我们分享。

施飞驰
2023-03-14

您的Hibernate实体要么使用< code>@Version注释,要么定义< code >

如果您像您所描述的那样显式地使用悲观锁定,那么删除上面的内容应该可以解决您的问题。

更多信息在这里

 类似资料:
  • 我试图理解Hibernate中的悲观锁定机制(通过MySQL DB)。 我尝试运行以下示例: 但它并没有给我一个错误,而是执行得很好。是不是我误解了什么概念。这种行为正常吗? 我能够完美地测试乐观锁定,那么对于悲观锁定,是对概念有一些误解,还是我的代码缺少了一些东西。

  • 问题内容: 我在java下有spring项目,使用hibernate查询,我喜欢使用悲观锁定。 在Spring + Hibernate中如何进行悲观锁定? 编辑: 问题: 我想在一个方法中使用悲观锁定,并且我将此方法称为从不同的方法。当我从第一个方法调用它时,悲观的工作效果很好,但是当我从第二个方法调用它时,它给出了(无法提交事务) 例外: 问题答案: http://www.amicabile.c

  • 我上面代码的日志是: 数据库也会更新。为什么lock()不工作?不是在lock()之后其他实例无法更新吗?还是别的什么?还是我错过了什么?

  • 我在一个游戏服务器上使用Hibernate for ORM,刚刚从每个工作单元的会话/事务切换到每个请求的会话/事务。 由于多个事务可能相互冲突,因此我使用悲观锁定。问题是我经常遇到僵局。现在我的问题是,是否可以在锁定特定实体实例的事务中显式定义范围,或者在指定锁定模式后是否锁定实例直到我提交事务? 如果后者成立,如果我不能任意调度锁定数据库中资源的方法的方法调用,我如何避免每个请求环境中的会话事

  • 问题内容: 我正在使用Criteria API检索hibernate中的对象列表。但是,我需要锁定那些对象,因为同时执行的另一个线程将获得确切的对象,并且在没有悲观锁定的情况下,只有一个线程会成功。 我尝试如下,但它不起作用。 更新 :我正在执行此语句之后的更新,以便我希望两个线程都读取不同的行,或者至少第二个线程应该等到第一个线程完成事务并离开锁。 hibernate生成的查询如下。 更新 :这

  • 这是令人难以置信的沮丧…我正在使用Couchbase v3 C API,我遇到了一个问题,这个问题可以通过使用他们的“悲观”锁定来完美解决。经过一番努力,我相信我终于找到了如何使用它锁定记录(使用,它大概需要一个以微秒为单位的参数),但是我不知道如何在完成后解锁记录,除了让它超时,这不是一个可以接受的解决方案。 这里的文档提供了一个示例,但出于某种未知的原因,它使用的是Javascript而不是C