Mybatis Update没执行

2018-11-13  本文已影响0人  风吟空城

引言

某项目采用“同步请求+异步回调”的方式,实现了用户账户信息的更新(余额、冻结金额等)。有时调用账户更新方法时,会出现数据库中账户信息没有修改的情况。但是,查看日志的时候,方法确实又执行了。结果就是导致用户账户信息异常。

思路

思路1

代码中出现了以下两种情形:

在经查看日志信息和其他级联数据时,发现都不是以上两种情形。

思路2

2个事务同时执行,前者事务被后者事务覆盖,出现了脏写。

经过仔细查看代码,发现是2个功能事务同时执行,其中一方加了悲观锁,一方没有加锁。下面通过一个简单的示例,描述下问题是如何产生的。

示例:用户账户冻结金额

方法一

public boolean changeAccount(Long id){
    //...省略部分代码
    //数据库加悲观锁 frozen = 100
    Account account = accountDao.getByIdForUpdate(id);
    account.setFrozen(account.getFrozen() + 100);
    //frozen = 200
    accountDao.modify(account);
    //...省略部分代码
}

方法二

public Account getByOwnerId(Long ownerId , HttpServletRequest request) {
    //...省略部分代码
    //更新余额 frozen = 100               
    account.setBalance(Double.valueOf(results.get("balance").toString()));
    //frozen = 100
    accountDao.modify(account);
    //...省略部分代码   
}

modify()是一个全量更新的方法

方法二在获取到account(frozen = 100)时,方法一(frozen = 100)将表锁住,此时,方法二无法继续进行下去,只能等待方法一执行完。方法一在更新完frozen信息后,释放锁,此时frozen = 200。方法二继续执行,将frozen还原到100。
至此,就造成数据库的脏写。同时,也造成了一个假象:我的方法执行后,数据库数据没有更新。

解决办法

给数据库字段加上版本号,更新时指定数据的版本号去更新,即使用乐观锁。

广大朋友有其他的解决办法的,还希望能指出,谢谢!

上一篇下一篇

猜你喜欢

热点阅读