Java 如何处理;StaleObjectStateException“;在带有悲观锁定的Hibernate中?

Java 如何处理;StaleObjectStateException“;在带有悲观锁定的Hibernate中?,java,mysql,hibernate,Java,Mysql,Hibernate,编辑: 在本例中,由于我使用的是“版本”注释,所以我使用的是乐观锁定,而不是悲观锁定。 如果我删除版本并因此禁用乐观锁定。悲观锁定接管了系统,性能显著降低 因此,我想我必须生活在乐观锁定和偶尔的例外中。有更好的解决办法吗 原件: 我目前通过ajp在Apache2.2负载平衡器中有多个tomcat实例。后端系统是hibernate。系统为多个用户和请求提供服务,对于请求,系统将从用户帐户中扣除一个信用。 我使用hibernate的悲观锁定来管理信用扣减。 在日志中,我不时从user account

编辑: 在本例中,由于我使用的是“版本”注释,所以我使用的是乐观锁定,而不是悲观锁定。
如果我删除版本并因此禁用乐观锁定。悲观锁定接管了系统,性能显著降低
因此,我想我必须生活在乐观锁定和偶尔的例外中。有更好的解决办法吗

原件: 我目前通过ajp在Apache2.2负载平衡器中有多个tomcat实例。后端系统是hibernate。系统为多个用户和请求提供服务,对于请求,系统将从用户帐户中扣除一个信用。

我使用hibernate的悲观锁定来管理信用扣减。
在日志中,我不时从user account对象中获得以下信息:

org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) org.hibernate.StaleObjectStateException:行被另一个事务更新或删除(或未保存的值映射不正确) 代码

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;
    }
私有布尔decUserAccountQuota(UserAccount UserAccount,int creditDec){

如果(userAccount.getBalance()您的Hibernate实体正在XML Hibernate映射中使用
@Version
注释或定义
。这将启用Hibernate提供的乐观锁定

如果您如前所述显式地使用悲观锁定,那么删除上述选项应该可以解决您的问题


更多信息

在您的代码中,尽管您将其指定为,但下一行没有采用悲观锁,因为您的数据库不支持选择..进行更新

manager.refresh(userAccount, LockMode.UPGRADE); 
因此,hibernate为userAccount使用另一种称为LockMode.READ的锁定模式,这是基于实体中的@Version属性的乐观锁定

我搜索了一下,看看是否有其他方法可以强迫hibernate对您的场景进行悲观锁定,但看起来不可能

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

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


如果您找到了任何替代解决方案,请与他人分享。

从您的描述中,听起来您实际上在使用乐观锁定?区别在中进行了描述。@Gustav Grusell:请查看更新在
用户帐户中的
映射,该映射位于注释为
@Version
的字段上。如果是,hibernate将l在事务之间执行乐观锁定。@Serge Ballesta:我应该删除版本和乐观锁定吗?您的异常无论如何都很奇怪,因为在单个事务中,您执行的选择、修改和更新操作应该总是有效的。什么是
管理器
类?在其他地方删除乐观锁定安全吗?decUserAccountQuota是只有执行写入操作的事务。但是,在该方法之前、期间和之后还有其他执行读取操作的事务。根据您的编辑,您最初的问题似乎已经得到了回答。使用乐观锁定并不太糟糕。事实上,与悲观锁定相比,它通常是首选,因为它具有良好的可扩展性和工作效率ks在读有时写的情况下尤其出色(如Hibernate文档中所述)。我可以想到使用悲观锁定的一个原因是,当您想在事务的“早期”知道失败时。将manager.refresh(userAccount,LockMode.UPGRDE)更改为manager.lock(userAccount,LockMode.UPGRADE)后;StaleObjectStateException现在经常出现在manager.lock上。我想这是因为userAccount被早期的读取操作以光学方式锁定了。有什么解决方案吗?@user121196对此表示抱歉。请检查您的DB是否支持选择..作为更新选项,以及您对DB使用的方言是什么