首頁  >  文章  >  Java  >  怎麼使用MyBatisPlus+SpringBoot實現樂觀鎖定功能

怎麼使用MyBatisPlus+SpringBoot實現樂觀鎖定功能

WBOY
WBOY轉載
2023-05-11 20:46:11843瀏覽

    一、商城資料不一致的場景

    如果商城中有一件商品,成本價是80元,售價是100元。經理先是通知小李,說你去把商品價格增加50元。小李正在玩遊戲,耽擱了一個小時。剛好一個小時後,經理覺得商品價格增加到150元,價格太高,可能會影響銷售。又通知小王,你把商品價格降低30元。

    此時,小李和小王同時操作商品後台系統。小李操作的時候,系統先取出商品價格100元;小王也在操作,取出的商品價格也是100元。小李將價格加了50元,並將100 50=150元存入了資料庫;小王將商品減了30元,並將100-30=70元存入了資料庫。是的,如果沒有鎖,小李的操作就完全被小王的覆蓋了。

    現在商品價格是70元,比成本價低10元。幾分鐘後,這個商品很快就賣了1千多件商品,老闆虧1萬多。

    二、示範此過程

    1、資料庫中增加商品表

    CREATE TABLE product
    (
        id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
        name VARCHAR(30) NULL DEFAULT NULL COMMENT '商品名称',
        price INT(11) DEFAULT 0 COMMENT '价格',
        version INT(11) DEFAULT 0 COMMENT '乐观锁版本号',
        PRIMARY KEY (id)
    );
    
    INSERT INTO product (id, NAME, price) VALUES (1, '笔记本', 100);

    2、建立實體類別

    @Data
    public class Product {
        private Long id;
        private String name;
        private Integer price;
        private Integer version;
    }

    3、建立Mapper

    public interface ProductMapper extends BaseMapper<Product> {
        
    }

    4、進行測試

    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class ProductVersionTest {
    	@Resource
    	private ProductMapper productMapper;
    	
    	@Test
    	public void testProductUpdate() {
    	
    	    //1、小李
    	    Product p1 = productMapper.selectById(1L);
    	
    	    //2、小王
    	    Product p2 = productMapper.selectById(1L);
    	
    	    //3、小李将价格加了50元,存入了数据库
    	    p1.setPrice(p1.getPrice() + 50);
    	    int result1 = productMapper.updateById(p1);
    	    System.out.println("小李修改结果:" + result1);
    	
    	    //4、小王将商品减了30元,存入了数据库
    	    p2.setPrice(p2.getPrice() - 30);
    	    int result2 = productMapper.updateById(p2);
    	    System.out.println("小王修改结果:" + result2);
    	
    	    //最后的结果
    	    Product p3 = productMapper.selectById(1L);
    	    System.out.println("最后的结果:" + p3.getPrice());
    	}
    }

    最後輸出的是70元,與經理預期的120元不同,導致虧損,如何防止這樣的異常發生,解決方案是使用樂觀鎖

    三、樂觀鎖定方案

    資料庫中新增version欄位:取出記錄時,取得目前version

    SELECT id,`name`,price,`version` FROM product WHERE id=1

    更新時,version 1,如果where語句中的version版本不對,則更新失敗

    UPDATE product SET price=price+50, `version`=`version` + 1 WHERE id=1 AND `version`=1

    四、樂觀鎖定實作流程

    1、修改實體類別

    新增@Version 註解

    @Version
    private Integer version;

    2、新增樂觀鎖定外掛程式

    @Configuration
    //@MapperScan("com.koo.modules.*.dao")
    public class MybatisPlusConfig {
    
        /**
         * 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
         */
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor() {
            MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
            interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
            //实现乐观锁,保证数据的准确性
            interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
            return interceptor;
        }
    
        @Bean
        public ConfigurationCustomizer configurationCustomizer() {
            return configuration -> configuration.setUseDeprecatedExecutor(false);
        }
    
    }

    3、最佳化流程

    (判斷第二次更新資料是否成功,不成功則重新取資料進行更新)

    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class ProductVersionTest {
    	@Resource
    	private ProductMapper productMapper;
    	
    	@Test
    	public void testProductUpdate() {
    	
    	    //1、小李
    	    Product p1 = productMapper.selectById(1L);
    	
    	    //2、小王
    	    Product p2 = productMapper.selectById(1L);
    	
    	    //3、小李将价格加了50元,存入了数据库
    	    p1.setPrice(p1.getPrice() + 50);
    	    int result1 = productMapper.updateById(p1);
    	    System.out.println("小李修改结果:" + result1);
    	
    	    //4、小王将商品减了30元,存入了数据库
    	    p2.setPrice(p2.getPrice() - 30);
    	    int result2 = productMapper.updateById(p2);
    	    System.out.println("小王修改结果:" + result2);
    	
    		if(result2 == 0){//更新失败,重试
    			    System.out.println("小王重试");
    			    //重新获取数据
    			    p2 = productMapper.selectById(1L);
    			    //更新
    			    p2.setPrice(p2.getPrice() - 30);
    			    productMapper.updateById(p2);
    		}
    	    //最后的结果
    	    Product p3 = productMapper.selectById(1L);
    	    System.out.println("最后的结果:" + p3.getPrice());
    	}
    }

    輸出結果為120,資料正確

    以上是怎麼使用MyBatisPlus+SpringBoot實現樂觀鎖定功能的詳細內容。更多資訊請關注PHP中文網其他相關文章!

    陳述:
    本文轉載於:yisu.com。如有侵權,請聯絡admin@php.cn刪除