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。
至此,就造成数据库的脏写。同时,也造成了一个假象:我的方法执行后,数据库数据没有更新。
解决办法
给数据库字段加上版本号,更新时指定数据的版本号去更新,即使用乐观锁。
广大朋友有其他的解决办法的,还希望能指出,谢谢!