mybatisPlus居然也要用占位符?逆天!讲讲mybatisPlus占位符的一个运用场景:exists语句

大家用过mybatisPlus的都应该知道,好用是真的好用。不用自己写sql,不用像mybatis一样写#{}的占位符用于生成预编译sql语句,只要写eq,gt,lt等方法就可以...

等等,谁说mybatisPlus不要用占位符?今天遇到了一个场景,就害我掏出了占位符。不过也可能是因为我太菜了,如果大家有更好的想法也欢迎提出。

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

这牵扯到两张表——商品表和订单表。

下图是商品表的两个字段,一个代表商品id,一个代表商品库存

下图是订单表的三个字段,分别代表订单id,购买人用户id,购买的商品id

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

那么如何只用一条sql语句,实现一人一单呢?逻辑其实不难,拿到请求(其中存放了用户信息和购买商品id)后,更新库存时,用exists语句判断订单表中是否存在对应用户购买此商品的订单,如果存在就下单失败,如果此用户对此商品还一条下单信息都没有,才下单成功。

于是就想到了运用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();

其中的notexists语句是这么写的:

.notExists("select * from tb_voucher_order where user_id = "  + userId +  " and voucher_id = " + voucherId)

等等,好像有点不对劲了,我怎么用拼接sql去了?。我在使用UpdateChainWrapper的notexists方法时,需要说明exists哪个userId和哪个voucherId,但不像where语句能用eq,exists语句好像并没有提供这样的语法糖,这个时候如果不用mybatisPlus的占位符的话,就只能拼接了,而拼接是不安全的。

终于进入正题了,那么mybatisPlus的占位符怎么写呢?是像mybatisPlus一样用#{}吗?还是像日志log.info一样,用{}占位呢?都不是。mybatis-plus的语法占位是这样的:

{索引}

{索引}是什么意思?其实索引就是参数的索引,比如我这条exists语句,用占位符前后对比:

不用占位符:.notExists("select * from tb_voucher_order where user_id = "  + userId +  " and voucher_id = " + voucherId)

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

然后变量userId对应{0}的位置,voucherId对应{1}的位置,并在发送sql时以预编译形式发送:

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)

在另一篇文章中了解到,.where,.and中也可以这么用占位符,如下图所示。不过我个人感觉exists语句用到占位符会多一点。

public List<Entity> selectByNameAndIdCard(String name, String idCard) {
    return super.selectList(new EntityWrapper<Entity>()
                .where("name = {0}", name)
                .and("idCard = {0}", idCard)
    );
}

链接:

[JAVA] (框架) mybatis-plus 语法占位_mybatisplus 站位-CSDN博客

最后还有一个提醒:这个业务情境只是我学习过程中遇到的,所运用的并发查库策略不太好,实际情境下一定不要这样高并发查库,个人测试100请求/s查库可能导致死锁。本文主要是分享一下exists需要的mybatisPlus占位符的运用,如果大家觉得有不需要占位符的方法,欢迎在评论区讨论。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值