问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

MyBatis Plus 占位符的运用场景:Exists 语句

创作时间:
作者:
@小白创作中心

MyBatis Plus 占位符的运用场景:Exists 语句

引用
CSDN
1.
https://blog.csdn.net/wyx1473343124/article/details/140655726

在使用MyBatis Plus时,通常不需要手动编写SQL语句,也不需要像MyBatis一样使用#{}占位符来生成预编译SQL语句。但是,在某些特定场景下,MyBatis Plus仍然需要使用占位符。本文将通过一个实际案例,介绍MyBatis Plus中占位符的使用方法。

业务场景

在实现秒杀下单功能时,需要保证一人一单的功能。也就是说,同一个用户抢购某件商品只能抢购一次,再下单就要返回错误信息。在高并发情况下,如何保证同一个用户一秒内发起100次请求时,只有一个请求能够更新商品库存成功(即下单成功)?

这涉及到两张表:商品表和订单表。

问题分析

一般的思路是,先查询订单表,判断是否有该用户对该商品的订单,如果没有订单,说明可以下单,再更新商品表的stock库存。但是如果不加锁的话,这么做是不行的,查询和更新分开进行,容易导致高并发情况下的安全问题。

解决方案

为了实现一人一单的功能,可以使用not exists语句,只用一条update语句完成判断和更新。以下是具体的代码实现:

boolean res = seckillVoucherService.update()
        .setSql("stock = stock - 1")   // set stock = stock - 1,代表库存-1
        .eq("voucher_id", voucherId)  //找到对应的商品来扣减库存
        .gt("stock",0)  // 库存大于0才能扣减
        .notExists("select * from tb_voucher_order where user_id = " + userId + " and voucher_id = " + voucherId )
        .update();  

但是,直接拼接SQL字符串是不安全的。因此,需要使用MyBatis Plus的占位符功能。MyBatis Plus的占位符使用{索引}的形式,其中索引表示参数的位置。以下是使用占位符的代码示例:

.notExists("select * from tb_voucher_order where user_id = {0} and voucher_id = {1}", userId, voucherId)

在发送SQL时,MyBatis Plus会以预编译的形式发送:

UPDATE tb_seckill_voucher  
SET stock = stock - 1  
WHERE (voucher_id = ? AND stock > ? AND NOT EXISTS (select * from tb_voucher_order where user_id = ? and voucher_id = ?))  
Parameters: 4(Long), 0(Integer), 1010(Long), 4(Long)  

总结

本文通过一个实际案例,介绍了MyBatis Plus中占位符的使用方法。需要注意的是,本文所介绍的并发查库策略并不适用于实际生产环境,实际情境下一定不要这样高并发查库,个人测试100请求/s查库可能导致死锁。本文主要是分享一下exists需要的MyBatis Plus占位符的运用,如果大家觉得有不需要占位符的方法,欢迎在评论区讨论。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号