spring事务详解实例
Posted sqy123
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring事务详解实例相关的知识,希望对你有一定的参考价值。
在Spring中,事务有两种实现方式:
编程式事务管理: 编程式事务管理使用底层源码可实现更细粒度的事务控制。spring推荐使用TransactionTemplate,典型的模板模式。
申明式事务管理: 添加@Transactional注解,并定义传播机制+回滚策略。基于Spring AOP实现,本质是对方法前后进行拦截,
方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。
关于spring事务实现方式:
引用博文:https://www.cnblogs.com/dennyzhangdd/p/9708499.html
关于分布式锁的实现方式:
引用博文:https://www.cnblogs.com/dennyzhangdd/p/7133653.html
提供一个具体实例来说明如何使用spring事务
基于数据库锁实现
1.悲观锁:select for update(一致性锁定读)
查询官方文档如上图,事务内起作用的行锁。能够保证当前session事务所锁定的行不会被其他session所修改(这里的修改指更新或者删除)。 对读取的记录加X锁,即排它锁,其他事不能对上锁的行加任何锁。 BEGIN;(确保以下2步骤在一个事务中:) SELECT * FROM tb_product_stock WHERE product_id=1 FOR UPDATE--->product_id有索引,锁行.加锁 (注:条件字段必须有索引才能锁行,否则锁表,且最好用explain查看一下是否使用了索引,因为有一些会被优化掉最终没有使用索引) UPDATE tb_product_stock SET number=number-1 WHERE product_id=1--->更新库存-1.解锁 COMMIT;
2.乐观锁:版本控制
选一个字段作为版本控制字段,更新前查询一次,更新时该字段作为更新条件
。
不同业务场景,版本控制字段,可以0 1控制,也可以+1控制,也可以-1控制,这个随意。
BEGIN;(确保以下2步骤在一个事务中:) SELECT number FROM tb_product_stock WHERE product_id=1--》查询库存总数,不加锁 UPDATE tb_product_stock SET number=number-1 WHERE product_id=1 AND number=第一步查询到的库存数--》number字段作为版本控制字段 COMMIT;
场景举例:
卖商品,先查询库存>0,更新库存-1。
例如:一种商品,有两件库存,多人来下单买,一个人一次只能买一件商品
创建表biz_shoe
1 CREATE TABLE `biz_shoe` ( 2 `shoe_uuid` char(32) NOT NULL, 3 `inventory_number` int(11) DEFAULT NULL COMMENT ‘库存数量‘, 4 `is_putaway` int(1) DEFAULT NULL COMMENT ‘是否上架‘, 5 PRIMARY KEY (`shoe_uuid`) 6 ) ENGINE=InnoDB DEFAULT CHARSET=utf8
给库存赋值,如图
3.基于悲观锁实现
1 /** 2 * 悲观锁 3 * @param shoe 4 * @return 5 */ 6 @Transactional(添加spring事务注解) 7 @Override 8 public Integer vieShoe(BizShoe shoe) { 9 //抢单商品加悲观锁 10 BizShoe modleShoe = shoeMapper.lockForUpdate(shoe.getShoeUuid()); 11 //商品库存 12 Integer number = modleShoe.getInventoryNumber(); 13 System.out.println("库存:" + number); 14 System.out.println("线程名称:" + Thread.currentThread().getName()); 15 if (number != 0) { 16 number = number - 1; 17 System.out.println("剩余库存:" + number); 18 modleShoe.setInventoryNumber(number); 19 shoeMapper.updateByPrimaryKeySelective(modleShoe); 20 } else { 21 AssertUtil.isTrue(number == 0, StatusCode.NumberIsNull, StatusCode.NumberIsNull.getMessage()); 22 } 23 return number; 24 }
在mapping中具体sql语句
1 <select id="lockForUpdate" parameterType="java.lang.String" resultMap="BaseResultMap"> 2 3 SELECT <include refid="Base_Column_List" /> FROM `biz_shoe` WHERE shoe_uuid = #{shoeUuid,jdbcType=CHAR} FOR UPDATE; 4 5 </select>
4.基于乐观锁
1 /** 2 * 乐观锁 3 * @param shoe 4 * @return 5 */ 6 @Transactional 7 @Override 8 public Integer vieShoe(BizShoe shoe) { 9 10 //抢单商品加乐观锁 11 BizShoe modleShoe = shoeMapper.selectByPrimaryKey(shoe.getShoeUuid()); 12 //商品库存 13 Integer number = modleShoe.getInventoryNumber(); 14 System.out.println("库存:" + number); 15 System.out.println("线程名称:" + Thread.currentThread().getName()); 16 if(number >0){ 17 int susus = shoeMapper.updateByShoeUuidInventoryNumber(shoe.getShoeUuid(),number); 18 System.out.println("更新成功 "+susus); 19 if(susus==1){//更新成功才算抢单成功 20 //商品库存 21 BizShoe modleShoeNew = shoeMapper.selectByPrimaryKey(shoe.getShoeUuid()); 22 System.out.println("新库存:" + modleShoeNew.getInventoryNumber()); 23 }else{ 24 AssertUtil.isTrue(true, StatusCode.NumberIsNull, StatusCode.NumberIsNull.getMessage()); 25 } 26 }else { 27 AssertUtil.isTrue(true, StatusCode.NumberIsNull, StatusCode.NumberIsNull.getMessage()); 28 } 29 return number; 30 }
sql语句
1 <update id="updateByShoeUuidInventoryNumber" parameterType="com.zstax.springtest.bean.BizShoe"> 2 3 UPDATE biz_shoe SET inventory_number=inventory_number-1 WHERE 4 shoe_uuid=#{shoeUuid,jdbcType=CHAR} and inventory_number=#{InventoryNumber,jdbcType=INTEGER} 5 6 </update>
以上是关于spring事务详解实例的主要内容,如果未能解决你的问题,请参考以下文章