业务逻辑层面上的事务控制

1、问题介绍

今天在工作上遇到一个问题:
数据库表 TABLE 中有一个字段 NUM,现要将它从数据库中取出来,经过一番运算后,再将其存入 TABLE 表中。
我们的问题就在于,在并发情况下,我们取出来的 NUM 也许会在我们进行运算的时候被其他人修改掉,而当我们将运算后的数据存入表中时,就会覆盖其他人的操作。

2、问题琢磨

当运算只是简单的加减运算的时候,我们考虑利用sql语句的特性,直接对其修改,如下:

update TABLE
set NUM = NUM + 1
where ID = #{id}

当MyBatis执行此sql语句时,会自动对 NUM 字段进行加锁,防止其他用户的修改。
但是这种解决方法只适用于能在一条 sql 语句中完成运算的情况,当运算过程中夹杂其他数据库操作时,就得借用其他字段来完成事务了。

3、解决方案

举例如下:

  1. 在 TABLE 表中新增一个 VERSION 字段
  2. 从表中取出 NUM 和 VERSION 字段,NUM 值为 1,VERSION 值为 1
  3. 对 NUM 值进行运算,其中夹杂其他数据库操作,此时 NUM 值变为了 2
  4. 使用如下 SQL 语句对表数据进行更新:
	update TABLE
    set NUM = #{num}, VERSION = VERSION + 1
    where
    ID = #{id}
    and VERSION = #{version}

4、方案原理

根据上述 SQL 语句可看出当 NUM 字段进行修改的时候 VERSION字段也会同时进行修改。
现假设两种情况

  • 在运算过程中没有其他人对NUM进行修改,那么表中的 VERSION 值并未被修改,该 SQL 语句可以从表中找到应该被修改的数据行,从而对 NUM 和 VERSION 进行修改。
  • 在运算过程中有其他人对NUM进行了修改,那么表中的 VERSION 值就会被修改,比如修改为了 2,那么上述 SQL 语句在运行的时候,找的还是 VERSION 为 1 的数据行,但此时 VERSION 值已经变成 2 了,从而就会找不到该数据行,以此避免其他人对 NUM 字段的修改被当前 SQL 语句覆盖掉。

5、注意事项

一般来说我们写代码的时候,每个方法都是具备一个事务,当方法内部有异常发生的时候,就会进行事务回滚。而当我们使用上述解决方案时,该 SQL 语句只是找不到需要被修改的数据行,而不是抛出异常,因此当我们执行该语句时,要对其返回参数进行判断,如果返回参数为 0,也就是 SQL 语句更新影响的行数为 0,即表示在运算过程中有其他人对 NUM 值进行了修改,那么我们要手动抛出一个异常,以此触发事务回滚,以便用户重新操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值