モールに商品がある場合、原価は 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 元以上を失いました。
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);
@Data public class Product { private Long id; private String name; private Integer price; private Integer version; }
public interface ProductMapper extends BaseMapper<Product> { }
@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 元と異なり、損失が発生しました。このような異常が発生しないようにするにはどうすればよいですか?解決策は、オプティミスティック ロックを使用することです
データベースにバージョン フィールドを追加します: レコードをフェッチするときに、現在のバージョンを取得します
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
Add @バージョンアノテーション
@Version private Integer version;
@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); } }
(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 サイトの他の関連記事を参照してください。