mybatis乐观锁的实现:使用版本控制并发
导读:
万事洞明皆学问,软件的版本号里面也有学问,所以祭出了《一文读懂MyBatis的版本号格式:major.minor.patch》,欢迎大家品读。
说到“版本号”,不仅用于表示软件的版本,还可以用作数据库的并发控制,这就是本文所要讲述的主要问题。欢迎各位读者驻足鉴赏。
1、并发的控制策略:
控制并发采用的策略通常分为乐观锁和悲观锁。
乐观锁的定义:顾名思义,对加锁持有一种乐观的态度,即先进行业务操作,不到最后一步不进行加锁,乐观地认为加锁一定会成功的,在最后一步更新数据的时候再进行加锁。
悲观锁的定义:正如其名字一样,悲观锁对数据加锁持有一种悲观的态度。因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。
简言之,
乐观锁:不是在数据库端锁住的,而是程序端控制的。此时可以在mybatis中实现乐观锁机制。
悲观锁:在数据库里面锁住,类似for update查询。
2、乐观锁的实现:
乐观锁的实现通常有两种方式:版本号字段和时间戳字段。
(1)版本(version)字段:
更新的时候给版本号字段加上1,然后UPDATE会返回一个更新结果的行数,通过这个行数去判断,如下所示:
UPDATE T_USER u
SET u.userName = #userName#, u.version = u.version + 1
WHERE u.userId = #userId# AND u.version = #version#
程序实现逻辑为:
if(rowsUpdated= =0)
{
throws new OptimisticLockingFailureException();
}
如果更新执行返回的数量是 0 表示产生并发问题了,则抛出乐观锁并发修改异常,需要重新获得最新的数据后再进行更新操作。
(2)时间戳(timestamps):
第二种实现方式和第一种差不多,同样是在需要乐观锁控制的table中增加一个字段,名称无所谓,字段类型使用时间戳(timestamp),和上面的version类似,也是在更新提交的时候检查当前数据库中数据的时间戳和自己更新前取到的时间戳进行对比,如果一致则OK,否则就是版本冲突。
此方案有缺点,就是当并发事务时间间隔小于当前系统平台的最小时间单位时,会发生覆盖前一个事务结果的问题。
3、Mybatis version乐观锁实现并发控制
<insert id="isExistsUser" >
select count(1) from t_users where userId = #{id}
</insert>
<select id="selectVersion" parameterType="user" resultType="long" >
select version from t_users where userName = #{userName}
</select>
<update id="updateByVersion" parameterType="user">
update t_users set version=version+1, password= #{password} where userName = #{userName} and version=#{version}
</update>
<select id="selectUsers" resultType="int">
select count(1)
from t_users
where id = #{id}
</select>
程序逻辑实现:
int userCount = UserMapper.selectUsers(id);
if (userCount==0)
{
Long version = UserMapper.selectVersion(user);
int call = 0;
user.setVersion(version);
while (UserMapper.updateByVersion(user)==0)
{
if (call++==3)
{
break;
}
}
}