化繁为简,MP 中条件构造器的使用,极其详细

1.1 MyBatis - Plus 中的条件构造器

首先,开始前引入 MP 官网的一段警告 :

不支持以及不赞成在 RPC 调用中把 Wrapper 进行传输

  1. wrapper 很重
  2. 传输 wrapper 可以类比为你的 controller 用 map 接收值(开发一时爽,维护火葬场)
  3. 正确的 RPC 调用姿势是写一个 DTO 进行传输,被调用方再根据 DTO 执行相应的操作
  4. 我们拒绝接受任何关于 RPC 传输 Wrapper 报错相关的 issue 甚至 pr

警告的意思大概是说:

  • Wrapper 这个抽象类很重,说白了就是很复杂,在 RPC 调用中更多的还是建议去建一个 DTO 去传参(至于在 HTTP 调用中是否建议,没去了解过,但估计是支持的吧…)

  • 还有一个大概就是说,在使用 Wrapper 这个抽象类当入参时如果出现报错,那么 MP 应该是不作解释的

好了,这个稍微注意一下便好。

  1. MP 中的条件构造器,其实就围绕着两个类去进行:QueryWrapper(LambdaQueryWrapper) 以及一个 UpdateWrapper(LambdaUpdateWrapper) ,其中如果你想使用 JDK8 引入的 Lambda 语法去进行代码的书写 ,就使用括号里的。

  2. 因为 QueryWrapperUpdateWrapper 其中的方法都相差不大,这里只拿 QueryWrapper 去举例说明,且 Lambda 语法的形式其实也是一样的,只是更换了一种书写语法而已。

  3. 我们可以在 QueryWrapper 的源码中看到,这个类继承了一个 AbstractWrapper ,这个类主要的作用是生成了 SQL 中的 WHERE 条件,封装了查询的条件;而在点到 AbstractWrapper 这个抽象类里面,你会发现这个抽象类继承了上面警告中提到的 Wrapper 类,这个就是这几个类中的真正老大 — 最顶级父类了,一个条件构造的抽象类

    • QueryWrapper 我们经常使用的,已经封装好了的查询条件构造器
    • AbstractWrapper 这个封装了查询的条件,即 SQL 中的 WHERE 叉叉叉
    • Wrapper 这个就是老大了,也就是开山鼻祖,开辟了条件构造的大门,让后辈在原有的基础上不断的扩充延展
  4. 好了,其实扯了这么多有的没的的背景,我们用来用去的其实就是两个:QueryWrapperUpdateWrapper ,如果想使用 Lambda 语法就是对应前面加了个 Lambda 的那两个。所以归根结底我们弄懂怎么使用其中一个,另外一个也就水到渠成自然而然的便懂了

  5. 在正式演示之前,先说明一下,这里演示的可能只是一些比较常用的方法,如果想看全面一些的方法,可以上 MP 官网上瞅瞅,全写着呢,也可以看我以前根据官网写的博客:MyBatis-Plus 方法罗列 http://t.csdn.cn/ggWpp

  6. 且以下操作是在上一篇文章的基础下往后进行的,没有表结构的可以先看一下我的上一篇文章:化繁为简,MyBatis-Plus 里面的增删改查 http://t.csdn.cn/amMFZ

  7. 还有一个,如果一直按照我上一篇文章进行,按理来说是开启了 MP 中的逻辑删除的,在下面的演示中建议用不上这个,所以把那个注解 @TableLogic(value = "0",delval = "1") 给注释了先,或者你也可以手动给数据库中 is_delete 这个字段全给写上 0 ,表示未删除的。我这里是把注解给注释掉了。当前的数据:

    在这里插入图片描述

1.2 比较

  • eq — 实际上就是 ‘equalts’ 嘛,等于 的意思(用的最多)
  • ne — "not equalts"嘛,这个是 不等于 的意思
  • le — 小于等于
  • ge — 大于等于
  • lt — 小于
  • gt — 大于
  1. 可能有人会说,这些现在看着还好,等会儿一眨眼又不记得了~其实还好,这些还挺好记的,反正带 e 的都和等于有关系,有 n 的就是 not 嘛

  2. 至于小于还是大于,对应英文应该是 little 和 great 吧,不过这个用的相对来说是比较少一点的

  3. 不用太过在意,记不住没关系,大不了用到的时候面向百度嘛!

  4. 为了省事,我这里还是使用 单元测试 进行,就不用 Postman 模拟调接口了

    (1) 场景一:找出用户年龄为18岁的人

        @Test
        public void test10() {
            QueryWrapper<User> qw = new QueryWrapper<>();
            /*
            1. 参数一:数据库的字段名。
            2. 参数二:就是你的条件了
            3. 等价于 SQL:SELECT * FROM user WHERE age = 18
             */
            qw.eq("age", 18);
            
            List<User> userList = userMapper.selectList(qw);
            userList.forEach(user -> System.out.println("user = " + user));
        }
    

    结果就不粘出来了,我这里查出有两条数据,一条 name = 逍遥,一条 name = 花魁;

    (2) 场景二:找出用户年龄为18,并且名字为逍遥的人

        @Test
        public void test10() {
            QueryWrapper<User> qw = new QueryWrapper<>();
            /*
            1. 参数一:数据库的字段名。
            2. 参数二:就是你的条件了
            3. 等价于 SQL:SELECT * FROM user WHERE age = 18 AND name = '逍遥'
             */
            qw.eq("age", 18).eq("name", "逍遥");
    //        qw.eq("age", 18);
    //        qw.eq("name", "逍遥");
            List<User> userList = userMapper.selectList(qw);
            userList.forEach(user -> System.out.println("user = " + user));
        }
    

    有两种方式,一种可以在原来的条件后面一直去点方法,MP 会在生成的 SQL 中去加上 AND;第二种就是如果觉得一直点自己的逻辑不清晰的话,想要分开一步步去写,也可以,如我上面注释了那样,可以另起一行去点出方法,一直到能够实现你的需求为止。

    但显而易见,第一种明显优雅很多,所以更多的还是推荐第一种方式

    (3) 场景三:找出用户年龄小于40岁的人

        @Test
        public void test10() {
            QueryWrapper<User> qw = new QueryWrapper<>();
            /*
            1. 参数一:数据库的字段名。
            2. 参数二:就是你的条件了
            3. 等价于 SQL:SELECT * FROM user WHERE age < 40
             */
            qw.lt("age", 40);
            List<User> userList = userMapper.selectList(qw);
            userList.forEach(user -> System.out.println("user = " + user));
        }
    

    在这里插入图片描述

    可以看到,年龄都是小于40的,其他几个都是一样,换汤不换药,可以多去尝试一下

1.3 范围

  • between — 与 SQL 中 between 叉叉 and 叉叉 一样的意思
  • in — 与 SQL 中 in 一样,包含
  • inSql — 这个是在 in 里面写 SQL 语句的意思
  • notBetween/notIn/notInSql — 上面几个取反的意思
  1. 这几个方法就与我们平常写的 SQL 没啥区别了,用法也差不多。至于说哪几个用的多一点,我个人感觉,ininSql 会稍微多一点,但 between 一样不少,但这个一般用于对时间进行判断

  2. 废话不多说,直接开始吧

    (1) 场景四:找出下面用户中年龄在 30~40 岁之间的

        @Test
        public void test11(){
            QueryWrapper<User> qw = new QueryWrapper<>();
            // 对应的SQL: SELECT * FROM user WHERE age BETWEEN 30 AND 40
            qw.between("age", 30, 40);
    
            List<User> userList = userMapper.selectList(qw);
            userList.forEach(user -> System.out.println("user = " + user));
        }
    

    此外,就像刚才说的,between 这个一般是用在时间判断上面的,但我们这里数据不是很完整,所以在这里就大致的说一下。

    比如说我们有这样一个场景:展示出还没过期的优惠券信息。那么你的优惠券表中肯定会有对应的一个创建时间和一个过期时间,那么此时的就是应该这样写了:qw.between("现在的时间", "创建时间", "过期时间") ,场景诸如此类有很多…

    (2) 场景五:找出用户年龄在 18,30,33,34 之间的人(年龄是啥不重要,因为是一个包含关系,包含在内就显示,否之则啥也没有)

        @Test
        public void test11(){
            QueryWrapper<User> qw = new QueryWrapper<>();
            // 对应的SQL: SELECT * FROM user WHERE age IN(18, 30, 33, 34)
            qw.in("age", 18, 30, 33, 34);
    
            List<User> userList = userMapper.selectList(qw);
            userList.forEach(user -> System.out.println("user = " + user));
        }
    

    在这里插入图片描述

    (3) 场景六:这个还真不太好举例子… 找出用户年龄小于40岁的人(上面场景三也是这个,不过这个是用另一种方式,先看用法吧)

        @Test
        public void test11(){
            QueryWrapper<User> qw = new QueryWrapper<>();
            // 对应的SQL: SELECT * FROM user WHERE id IN(SELECT id FROM user WHERE age < 40)
            String sql = "SELECT id FROM user WHERE age < 40";
            qw.inSql("id", sql);
    
            List<User> userList = userMapper.selectList(qw);
            userList.forEach(user -> System.out.println("user = " + user));
        }
    

    为什么说这个例子难举呢,因为上面这个场景实际上是不符合 inSql 的用法的,但我们的演示表也确实不是很理想的满足到 inSql 这个条件的应用

    inSql 这个方法,一般是表中有一些关联的字段,而你这些关联的字段的数据需要从别的表拿或者是通过入参传过来。虽然很绕,但是这些都无所谓,重点是你想要实现的效果是:只展示一些需要从别的地方拿过来的数据,而这些数据是与你当前的实体类对应的表有关联过线的,所以就要这样写:qw.inSql("这个就是你实体类所对应的关联关系的字段", "而这个就是你需要从别的地方拿到的一些列数据了");

1.4 模糊匹配

  • like — 跟 SQL 中一样,LIKE ‘%叉叉叉%’
  • notLike/likeLeft/likeRight — 这几个就很简单了,且用的不多,就不过多叙述了
  1. 这个就是我们一般在 SQL 中使用 CONCAT()函数 去拼接的一个模糊查询了

  2. 这个比较简单,也是直接用就可以了

    (1) 场景七:找到名字中有一个月字的人

        @Test
        public void test12(){
            QueryWrapper<User> qw = new QueryWrapper<>();
            // 对应的SQL: SELECT * FROM user WHERE name LIKE %月%
            qw.like("name", "月");
    
            List<User> userList = userMapper.selectList(qw);
            userList.forEach(user -> System.out.println("user = " + user));
        }
    

    结果全是"拜月"…

1.5 空值比较

  • isNull — 等价于 SQL 中的 IS NULL
  • isNotNull — 等价于 SQL 中的 IS NOT NULL
  1. 这个也是比较简单,直接上场景

    (1) 场景八:筛选出列表中创建时间为空的数据

        @Test
        public void test12(){
            QueryWrapper<User> qw = new QueryWrapper<>();
            // 对应的SQL: SELECT * FROM user WHERE create_time IS NULL
            qw.isNull("create_time");
    
            List<User> userList = userMapper.selectList(qw);
            userList.forEach(user -> System.out.println("user = " + user));
        }
    

1.6 分组和排序

  • groupBy — SQL 中的 GROUP BY 分组
  • orderByAsc
  • orderByDesc
  • having — SQL 中的 HAVING(sql 语句)
  1. 前面三个就很见词明义了,最后一个有点类似 in ,但这个我用的很少,几乎没用过,所以也没有什么场景可以分享的

  2. 而 groupBy 那些呢用法也与前面相差不大,但这个意思和使用的场景,更多的还是需要结合实际的场景去搭配使用;在这里就没有这样的场景去演示了

  3. 至于 orderBy 那两个,在实操中偶尔会用到,根据某某字段去排序。比如说有些时候,类似评论,肯定是最新的评论或者点赞数最多的评论在最前面,而这个时候,就可以根据一个评论的时间或者点赞数量去排序了

    (1) 场景九:年龄从大到小进行排序

        @Test
        public void test12(){
            QueryWrapper<User> qw = new QueryWrapper<>();
            // 对应的SQL: SELECT * FROM user order by age desc
            qw.orderByDesc("age");
    
            List<User> userList = userMapper.selectList(qw);
            userList.forEach(user -> System.out.println("user = " + user));
        }
    

1.7 小结

  1. 以上就是 MP 中比较常用的条件构造器的使用了,当然,在实际开发中显然业务会复杂的多,那么这个时候就需要酌情的去斟酌一下,是自己去写 SQL 效率高还是用 MP 的效率高些
  2. 其实个人觉得,MP 确实在一定程度上去提高了我们开发的效率,但是回归到本质还是需要去熟练的掌握 SQL 怎样写,因为 MP 只是帮我们封装了而已,但在一些较为复杂的多表操作时,终归还是需要自己去写 SQL 的
  3. 此外,当我们 SQL 能力强起来后,对 MP 的使用也会有一种水到渠成的感觉,使用起来更加得心应手
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天怎么不会塌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值