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

时间:2014-08-20 04:25:37

标签: java mysql hibernate

修改 事实证明,因为我使用的是“版本”注释,所以我使用乐观锁定,而不是悲观锁定。
如果我删除版本,因此禁用乐观锁定。悲观锁定接管并且性能显着下降。
所以我想我必须忍受乐观的锁定和偶尔的异常。有更好的解决方案吗?

原始 我目前通过ajp在apache 2.2负载均衡器中有多个tomcat实例。后端系统是休眠的。系统为多个用户和请求提供服务,请求从用户的帐户中扣除一个信用。

我使用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 个答案:

答案 0 :(得分:0)

您的Hibernate实体使用@Version注释或在XML Hibernate映射中定义<version>。这将启用Hibernate提供的乐观锁定。

如果您正如您所描述的那样明确使用悲观锁定,删除上述内容应该可以解决您的问题。

更多信息here

答案 1 :(得分:0)

在你的代码中,下面的行没有采用悲观的锁,虽然你指定它,因为你的数据库不支持SELECT .. FOR UPDATE。

manager.refresh(userAccount, LockMode.UPGRADE); 

因此,hibernate为userAccount使用名为LockMode.READ的替代锁定模式,该模式是基于实体中@Version属性的乐观锁定。

我搜索了是否有任何替代方法强制hibernate为你的场景采取悲观锁定,但看起来它不可能。

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

在userAccount对象上同步。虽然这似乎会影响性能,但只有当同一用户放置太多并发请求时才会发生这种情况。同时放弃一点性能以确保用户不需要抛出异常并重新进行重试将是理想的情况。

如果您找到了任何其他解决方案,请分享。