SpringBoot+MyBatis——基于PageHelper插件分页操作过程中全注解实现多条件模糊查询的一个注意点

在利用SpringBoot集成MyBatis实现简单的业务操作时,遇到了一个坑,记录一下。

一般来讲,实现多条件模糊查询的逻辑为:前端页面输入各项参数,提交查询请求时,参数通过RUL(或者请求体)传输到后端。后端提取出相关参数,注入至sql语句,实现多条件模糊查询。

Service层相关代码(本文省略Controller层代码)


    /**
     * 分页查询(基于此实现多条件模糊查询)
     */
    @Override
    public PageInfo<ProductInfo> splitPageForPageInfo(int pageNum, int pageSize, ProductInfo productInfo) {
        PageHelper.startPage(pageNum, pageSize);
        List<ProductInfo> productInfoList = productDao.selectAll(productInfo);
      //PageHelper相关配置已经在yml文件中声明,项目加载时会生成bean放入到Spring容器中
      //原sql语句后面会自动添加Limit
        PageInfo<ProductInfo> pageInfo = new PageInfo<>(productInfoList);
        return pageInfo;
    }

刚开始本人的Dao层代码如下所示

 @Select("<script>"
            +"select * from product_info"
            + "<where>"
            + "<if test='typeId !=null'> and type_id like CONCAT('%',#{typeId},'%')</if>"
            + "<if test='pName !=null'> and p_name like CONCAT('%',#{pName},'%')</if>"
            + "<if test='pContent !=null'> and p_content like CONCAT('%',#{pContent},'%')</if>"
            + "<if test='pNumber !=null'> and p_number like CONCAT('%',#{pNumber},'%')</if>"
            + "<if test='pPrice !=null'> and p_price like CONCAT('%',#{pPrice},'%')</if>"
            + "</where>"
            +"</script>"
    )
    List<ProductInfo> selectAll(ProductInfo productInfo);

注意到此处的<if>标签中,只包含了各个属性为null的情况,而忽略了它们为空串的情形,这个小细节将会导致后续页面无法正常显示数据,并且我们很难查出错误所在。比如本人在测试类中调试Service层splitPageForPageInfo()方法,代码与运行结果如下:

 @Test
    void testSplitPageForPageInfo() {
        ProductInfo productInfo = new ProductInfo();
        productInfo.setTypeId("电脑");
        PageInfo pageInfo = productService.splitPageForPageInfo(1, 3, productInfo);
        List<ProductInfo> productInfoList = pageInfo.getList();
        for (ProductInfo p : productInfoList) {
            System.out.println(p);
        }
    }
2021-12-30 09:30:23.273 DEBUG 11764 --- [           main] c.wangjf.dao.ProductDao.selectAll_COUNT  : ==>  Preparing: SELECT count(0) FROM product_info WHERE type_id LIKE CONCAT('%', ?, '%')
2021-12-30 09:30:23.299 DEBUG 11764 --- [           main] c.wangjf.dao.ProductDao.selectAll_COUNT  : ==> Parameters: 电脑(String)
2021-12-30 09:30:23.319 DEBUG 11764 --- [           main] c.wangjf.dao.ProductDao.selectAll_COUNT  : <==      Total: 1
2021-12-30 09:30:23.322 DEBUG 11764 --- [           main] com.wangjf.dao.ProductDao.selectAll      : ==>  Preparing: select * from product_info WHERE type_id like CONCAT('%',?,'%') LIMIT ?
2021-12-30 09:30:23.323 DEBUG 11764 --- [           main] com.wangjf.dao.ProductDao.selectAll      : ==> Parameters: 电脑(String), 3(Integer)
2021-12-30 09:30:23.326 DEBUG 11764 --- [           main] com.wangjf.dao.ProductDao.selectAll      : <==      Total: 2
ProductInfo(pId=4, pName=联想小新, pContent=dddd, pPrice=8000.0, pNumber=1, typeId=电脑)
ProductInfo(pId=13, pName=外星人, pContent=水, pPrice=10999.0, pNumber=1, typeId=电脑)

通过MyBatis日志输出可以看到,此代码逻辑下sql语句的运行顺序为:首先查询出type_id字段中含有''电脑"这个字符串的所有数据(大家自动忽略此处的数据名称和内容...此表是俺从上一个项目中随便捡来的...),再利用Limit语句分页。可以看到查询结果是正确的

而当我们将productInfo对象赋为null,理论上sql语句执行顺序为:因无条件匹配,直接在原sql语句后面加上Limit实现分页。从运行日志可以看出执行顺序也确实如此:

  @Test
    void testSplitPageForPageInfo() {
        //ProductInfo productInfo = new ProductInfo();
        //productInfo.setTypeId("电脑");
        PageInfo pageInfo = productService.splitPageForPageInfo(1, 3,null);
        List<ProductInfo> productInfoList = pageInfo.getList();
        for (ProductInfo p : productInfoList) {
            System.out.println(p);
        }
    }
2021-12-30 09:31:56.297 DEBUG 6404 --- [           main] c.wangjf.dao.ProductDao.selectAll_COUNT  : ==>  Preparing: SELECT count(0) FROM product_info
2021-12-30 09:31:56.324 DEBUG 6404 --- [           main] c.wangjf.dao.ProductDao.selectAll_COUNT  : ==> Parameters: 
2021-12-30 09:31:56.341 DEBUG 6404 --- [           main] c.wangjf.dao.ProductDao.selectAll_COUNT  : <==      Total: 1
2021-12-30 09:31:56.343 DEBUG 6404 --- [           main] com.wangjf.dao.ProductDao.selectAll      : ==>  Preparing: select * from product_info LIMIT ?
2021-12-30 09:31:56.344 DEBUG 6404 --- [           main] com.wangjf.dao.ProductDao.selectAll      : ==> Parameters: 3(Integer)
2021-12-30 09:31:56.346 DEBUG 6404 --- [           main] com.wangjf.dao.ProductDao.selectAll      : <==      Total: 3
ProductInfo(pId=1, pName=iphone14, pContent=128GB, pPrice=5999.0, pNumber=1, typeId=2)
ProductInfo(pId=2, pName=iphone15, pContent=256GB, pPrice=6799.0, pNumber=2, typeId=1)
ProductInfo(pId=3, pName=专用座机, pContent=null, pPrice=null, pNumber=null, typeId=手机)

为确保万无一失,本人还用Postman进行了测试

                                                            1.查询全部

2.按照pageNum=1,pageSize=3分页查询

3.基于条件typeId="电脑"进行模糊查询

上面三项测试都得到正确的结果.. 此时我感觉已经大功告成了!

但是当我将项目运行-->打开网页--->刷新.. 竟然GG了..

乍一看,似乎没啥问题,但是仔细一看.. 竟然漏掉了字段属性存在空值的一条数据(通过控制台打印的消息也可以看到total=6)

怎么可以容忍这样的事情发生?! 

经过漫长的寻觅.. (不再赘述本人的痛苦遭遇),终于发现原因为:在<if>标签中没有考虑数据为空串的情况.... 修改Dao层代码如下:

 @Select("<script>"
            +"select * from product_info"
            + "<where>"
            + "<if test='typeId !=null and typeId != \"\"'> and type_id like CONCAT('%',#{typeId},'%')</if>"
            + "<if test='pName !=null and pName != \"\"'> and p_name like CONCAT('%',#{pName},'%')</if>"
            + "<if test='pContent !=null and pContent != \"\"'> and p_content like CONCAT('%',#{pContent},'%')</if>"
            + "<if test='pNumber !=null and pNumber != \"\"'> and p_number like CONCAT('%',#{pNumber},'%')</if>"
            + "<if test='pPrice !=null and pPrice != \"\"'> and p_price like CONCAT('%',#{pPrice},'%')</if>"
            + "</where>"
            +"</script>"
    )

    List<ProductInfo> selectAll(ProductInfo productInfo);

最终可得正确网页显示(控制台输出total=7):

 多条件模糊查询功能也正常

 谨以本人第一篇CSDN来缅怀那逝去的一个夜晚.....

注:查询错误的具体原因分析

当我们没有在<if>标签中添加"空串"条件时,刷新网页,得到日志信息如下:

2021-12-30 10:44:03.460 DEBUG 18176 --- [nio-8080-exec-2] c.wangjf.dao.ProductDao.selectAll_COUNT  : ==>  Preparing: SELECT count(0) FROM product_info WHERE type_id LIKE CONCAT('%', ?, '%') AND p_name LIKE CONCAT('%', ?, '%') AND p_content LIKE CONCAT('%', ?, '%')
2021-12-30 10:44:03.476 DEBUG 18176 --- [nio-8080-exec-2] c.wangjf.dao.ProductDao.selectAll_COUNT  : ==> Parameters: (String), (String), (String)
2021-12-30 10:44:03.489 DEBUG 18176 --- [nio-8080-exec-2] c.wangjf.dao.ProductDao.selectAll_COUNT  : <==      Total: 1
2021-12-30 10:44:03.492 DEBUG 18176 --- [nio-8080-exec-2] com.wangjf.dao.ProductDao.selectAll      : ==>  Preparing: select * from product_info WHERE type_id like CONCAT('%',?,'%') and p_name like CONCAT('%',?,'%') and p_content like CONCAT('%',?,'%') LIMIT ?
2021-12-30 10:44:03.492 DEBUG 18176 --- [nio-8080-exec-2] com.wangjf.dao.ProductDao.selectAll      : ==> Parameters: (String), (String), (String), 3(Integer)
2021-12-30 10:44:03.495 DEBUG 18176 --- [nio-8080-exec-2] com.wangjf.dao.ProductDao.selectAll      : <==      Total: 3

我们可以看到整条sql语句是在<where>标签下的,众所周知,只有<where>标签内的条件成立,才会在拼接sql时加入WHERE关键字。所以日志结果表明,当前有条件满足了。并且我们很容易知道是以下三个条件满足了(也就是说typeId、pName、pContent这三个数据不为null

<if test='typeId !=null'>
<if test='pName !=null'>
<if test='pContent !=null'>

这样拼接得到的sql语句的查询结果就是6条数据..

这就奇怪了?单纯地刷新页面,理论上这些参数的值应该都为null啊?

既然测试类与Postman都运行正常,那只有是前端页面的问题了...

于是我发现在html文件中,当时为了方便,将这些属性的初始值都设为空串了(当然这边是无法直接将属性赋为null的,其会被当成"null"字符串传入),而我又理所当然地认为"空串"=null

                typeId: "",
                pName: "",
                pContent: "",
                pNumber: "",
                pPrice: ""

可为什么以下这两个条件还是被判定为不成立呢?

<if test='pNumber !=null'>
<if test='pPrice !=null'>

由此个人认为:若在实体类中定义为数值类型的属性(Integer、Double、Float),其被赋默认值为"空串"时,MyBatis底层判断为null,而在实体类中定义为字符类型的属性(String、Char),其被赋默认值为"空串"时,MyBatis底层判断为非null。这点欢迎大家去亲自实践验证...

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值