MyBatisPlus+SpringBootを使用して楽観的ロック機能を実装する方法

WBOY
リリース: 2023-05-11 20:46:11
転載
840 人が閲覧しました

    1. モールデータの不一致のシナリオ

    モールに商品がある場合、原価は 80 元、販売価格は 100 元です。 。マネージャーは最初にシャオ・リーさんに、商品の価格を50元値上げする必要があると伝えました。シャオ・リーはゲームをしていて1時間遅れた。ちょうど 1 時間後、マネージャーは商品の価格が 150 元に値上がりしており、高すぎて売り上げに影響するかもしれないと感じました。また、Xiao Wang に商品の価格を 30 元値下げすることを伝えます。

    現時点では、Xiao Li と Xiao Wang が製品のバックエンド システムを同時に操作しています。 Xiao Liが動作しているとき、システムは最初に製品価格100元を取り出しますが、Xiao Wangも動作しており、取り出される製品価格も100元です。 Xiao Li は価格に 50 元を追加して 100 50 = 150 元をデータベースに保存し、Xiao Wang は商品を 30 元値下げして 100-30 = 70 元をデータベースに保存しました。はい、ロックがない場合、Xiao Li の操作は Xiao Wang の操作によって完全にカバーされます。

    現在の製品価格は70元で、原価より10元安いです。数分後、この商品は 1,000 点以上がすぐに売れ、上司は 10,000 元以上を失いました。

    2. このプロセスをデモンストレーションします

    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. Test

    @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 元と異なり、損失が発生しました。このような異常が発生しないようにするにはどうすればよいですか?解決策は、オプティミスティック ロックを使用することです

    3. オプティミスティック ロック スキーム

    データベースにバージョン フィールドを追加します: レコードをフェッチするときに、現在のバージョンを取得します

    SELECT id,`name`,price,`version` FROM product WHERE id=1
    ログイン後にコピー

    更新するとき、バージョン 1、where ステートメントのバージョンが間違っている場合は、更新に失敗しました

    UPDATE product SET price=price+50, `version`=`version` + 1 WHERE id=1 AND `version`=1
    ログイン後にコピー

    4. オプティミスティック ロックの実装プロセス

    1. エンティティ クラスを変更します

    Add @バージョンアノテーション

    @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. 最適化処理

    (2回目のデータ更新が成功したかどうかを判定、失敗した場合は再取得更新用データ)

    @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 中国語 Web サイトの他の関連記事を参照してください。

    関連ラベル:
    ソース:yisu.com
    このウェブサイトの声明
    この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
    人気のチュートリアル
    詳細>
    最新のダウンロード
    詳細>
    ウェブエフェクト
    公式サイト
    サイト素材
    フロントエンドテンプレート
    私たちについて 免責事項 Sitemap
    PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!