关于mybatis多表查询构建通用的动态查询对象

1、动态查询使用

1.0、看在前面

对于我们后端小伙伴来说,增删改查,那肯定是信手拈来。不知道我们后端的小伙伴有没有遇到过这样的问题:就是比较复杂一点的功能,往往index页面的数据,都是会涉及多表查询的,这时候呢,我们想不写sql那肯定是不可以了,没法偷懒用mybatis-plus的方法了。我一般对于多表查询呢,都是post请求,写一个ReqDTO类来接收前端的参数,因为真的有的查询条件会有一二十个,甚至更多,所以这时候用get请求,url就长的太恐怖了。这个ReqDTO类呢,包含了一些页面上的参数和分页参数,然后我们拿到呢,按照参数在mybatis的xml文件中拼接动态sql语句,实现多表查询。

那么现在问题来了,如果现在突然接到一个任务,现在已经完成的所有的业务功能,凡是和xx有关系的,全部要再和另外一个权限表做连接查询,筛选出当前用户有权限的数据,不能看全部的数据了,这个就相当于,之前所有get请求查单表的,都废了,而且,每一个功能的查询字段都不一样,如果按照上面说的定义ReqDTO来一个一个改的话,那估计就累死了,而且前端用了哪些参数查询,你根本不知道,这一点一点对接的过程中,也是一个很大的时间成本。

所以我就想了个办法,使用post请求,还是json传参,只不过,我让前端自己查。怎么说呢,就是让前端自己写‘SQL’查询,哈哈哈,这样我就省事了啊,我不用了解他们用了哪些参数查询,他们想怎么查,自己放到入参里给我就完事。这样,我不就省事了吗,话不多说,上代码!

1.1、参数传递(json):
{
    "pageNum": 1, // 当前页
    "pageSize": 10, // 页大小
    "conditions": [
        {
            "columnName": "CREATE_USER_NAME", // 查询列列名
            "queryItem": {
                "conjunction": "AND", // 逻辑短路:AND、OR,可不传:默认为AND
                "operator": "like_right", // 查询符号
                "value": "胡" // 查询的值
            }// 查询条件子项
        },
        {
            "columnName": "CREATE_USER_NAME",
            "queryItem": {
                "conjunction": "OR",
                "operator": "like_left",
                "value": "声康"
            }
        },
        {
            "columnName": "THINGS_CHANGE",
            "queryItem": {
                "conjunction": "AND",
                "operator": "in",
                "value": [
                    "JPMsoft_SXBG_QZ",
                    "JPMsoft_SXBG_SJ"
                ]
            }
        }
    ], // 查询条件
    "orderBys": {
        "CREATE_TIME": "DESC,",
        "UPDATE_TIME": "DESC"
    } // 排序条件
}
1.2、后端代码
  • 1.2.1 动态查询条件类
/**
 * 动态查询条件
 *
 * @author GM
 * @date 2023/7/13 10:29
 * @description TODO
 */
@ToString
public class DynamicQueryCondition implements Serializable {

    // private Map<String, QueryItem> conditions;

    /**
     * 条件
     */
    private List<ColumnQueryItem> conditions;

    /**
     * 排序方式
     */
    private Map<String, String> orderBys;

    /**
     * 页大小 , 默认值 10
     */
    private Integer pageSize = 10;

    /**
     * 当前页 , 默认值 1
     */
    private Integer pageNum = 1;

    /**
     * 鉴权SQL
     */
    private String authSql;

    public DynamicQueryCondition() {
    }

    public DynamicQueryCondition(List<ColumnQueryItem> conditions,
                                 Map<String, String> orderBys,
                                 Integer pageSize,
                                 Integer pageNum,
                                 String authSql) {
        this.conditions = conditions;
        this.orderBys = orderBys;
        this.pageSize = pageSize;
        this.pageNum = pageNum;
        this.authSql = authSql;
    }

    public List<ColumnQueryItem> getConditions() {
        return conditions;
    }

    public void setConditions(List<ColumnQueryItem> conditions) {
        this.conditions = conditions;
    }

    public Map<String, String> getOrderBys() {
        return orderBys;
    }

    public void setOrderBys(Map<String, String> orderBys) {
        this.orderBys = orderBys;
    }

    public Integer getPageSize() {
        return pageSize;
    }

    public void setPageSize(Integer pageSize) {
        this.pageSize = pageSize;
    }

    public Integer getPageNum() {
        return pageNum;
    }

    public void setPageNum(Integer pageNum) {
        this.pageNum = pageNum;
    }

    public String getAuthSql() {
        return authSql;
    }

    public void setAuthSql(String authSql) {
        this.authSql = authSql;
    }

    /**
     * 组装查询条件
     *
     * @author GM
     * @date 2023/7/18 9:56
     * @description TODO
     */
    @ToString
    static class ColumnQueryItem {

        /**
         * 列名
         */
        private String columnName;

        /**
         * 查询项
         */
        private QueryItem queryItem;

        public ColumnQueryItem() {
        }

        public ColumnQueryItem(String columnName, QueryItem queryItem) {
            this.columnName = columnName;
            this.queryItem = queryItem;
        }

        public String getColumnName() {
            return columnName;
        }

        public void setColumnName(String columnName) {
            this.columnName = columnName;
        }

        public QueryItem getQueryItem() {
            return queryItem;
        }

        public void setQueryItem(QueryItem queryItem) {
            this.queryItem = queryItem;
        }


    }

    /**
     * 查询条件
     *
     * @author GM
     * @date 2023/7/13 10:29
     * @description TODO
     */
    @ToString
    static class QueryItem implements Serializable {

        /**
         * 查询连接词,默认AND
         */
        private String conjunction = "AND";

        /**
         * 操作符,如"like"、"eq"、"ne"、"gt"、"lt"、"ge"、"le"、"in"、"not in"等
         */
        private String operator;

        /**
         * 查询字段值
         */
        private Object value;

        public QueryItem() {
        }

        public QueryItem(String operator, Object value, String conjunction) {
            this.operator = operator;
            this.value = value;
            this.conjunction = conjunction;
        }

        public String getOperator() {
            return operator;
        }

        public void setOperator(String operator) {
            if (StrUtil.isNotBlank(operator)) {
                // 这里主要是在set方法中对比较运算符进行转换成小写操作
                // 因为下面xml中<when>标签中是用小写判断。
                this.operator = operator.toLowerCase(); 
            }
        }

        public Object getValue() {
            return value;
        }

        public void setValue(Object value) {
            this.value = value;
        }

        public String getConjunction() {
            return conjunction;
        }

        public void setConjunction(String conjunction) {
            // 连接运算符 AND OR 做转换为大写处理(主要是为了打印出来的sql看着舒服一点手动狗头)
            this.conjunction = conjunction.toUpperCase();
        }
    }


}
  • 1.2.2、对应mybatis的xml的Sql片段

    <!-- 动态拼接sql:查询片段 -->
    <if test="conditions != null and conditions.size() > 0">
        <foreach collection="conditions" item="item" index="">
            <choose>
                <!-- 模糊 ‘%xxx%’ -->
                <when test=" item.queryItem.operator == 'like' ">
                    ${item.queryItem.conjunction} mn.${item.columnName} LIKE CONCAT('%', #{item.queryItem.value},'%')
                 </when>
                 <!-- 模糊(右) ‘xxx%’ -->
                 <when test=" item.queryItem.operator == 'like_right' ">
                     ${item.queryItem.conjunction} mn.${item.columnName} LIKE CONCAT(#{item.queryItem.value}, '%')
                 </when>
                 <!-- 模糊(左) ‘%xxx’ -->
                 <when test=" item.queryItem.operator == 'like_left' ">
                      ${item.queryItem.conjunction} mn.${item.columnName} LIKE CONCAT('%', #{item.queryItem.value})
                 </when>
                 <!-- 等于 -->
                 <when test=" item.queryItem.operator == 'eq' ">
                            ${item.queryItem.conjunction} mn.${item.columnName} = #{item.queryItem.value}
                 </when>
                 <!-- 不等于 -->
                 <when test=" item.queryItem.operator == 'ne' ">
                      ${item.queryItem.conjunction} mn.${item.columnName} != #{item.queryItem.value}
                 </when>
                 <!-- 大于 -->
                 <when test=" item.queryItem.operator == 'gt' ">
                      ${item.queryItem.conjunction} mn.${item.columnName} &gt; #{item.queryItem.value}
                  </when>
                  <!-- 小于 -->
                  <when test=" item.queryItem.operator == 'lt' ">
                       ${item.queryItem.conjunction} mn.${item.columnName} &lt; #{item.queryItem.value}
                  </when>
                  <!-- 大于等于 -->
                  <when test=" item.queryItem.operator == 'ge' ">
                       ${item.queryItem.conjunction} mn.${item.columnName} &gt;= #{item.queryItem.value}
                  </when>
                  <!-- 小于等于 -->
                  <when test=" item.queryItem.operator == 'le' ">
                       ${item.queryItem.conjunction} mn.${item.columnName} &lt;= #{item.queryItem.value}
                  </when>
                  <!-- IN -->
                  <when test=" item.queryItem.operator == 'in' ">
                       <if test=" item.queryItem.value != null and item.queryItem.value.size() > 0">
                                ${item.queryItem.conjunction} mn.${item.columnName} IN
                                <foreach collection="item.queryItem.value" open="(" separator="," close=")" item="inItem" index="">
                                    #{inItem}
                                </foreach>
                       </if>
                   </when>
                   <!-- NOT IN -->
                   <when test=" item.queryItem.operator == 'not_in' ">
                        <if test=" item.queryItem.value != null and item.queryItem.value.size() > 0">
                                ${item.queryItem.conjunction} mn.${item.columnName} NOT IN
                                <foreach collection="item.queryItem.value" open="(" separator="," close=")" item="notInItem"
                                         index="">
                                    #{notInItem}
                                </foreach>
                        </if>
                   </when>
                   <!-- 添加更多操作符的处理 -->
                   <otherwise>
                        1 = 1
                   </otherwise>
                    </choose>
                </foreach>
            </if>
            <!-- 动态拼接sql:排序片段 -->
            <if test=" orderBys != null and orderBys.size() > 0">
                ORDER BY
                <foreach collection="orderBys.entrySet()" item="item" index="key">
                    <if test=" item != null and item != '' ">
                        mn.${key} ${item}
                    </if>
                </foreach>
            </if>
            <if test=" orderBys == null or orderBys.size() == 0">
                ORDER BY mn.CREATE_TIME DESC
            </if>
    

    前面的 mn.当前功能表的别名,为了方便起的一个统一的别名,因为连接词、字段名和查询值都是前端动态获取,所以这个片段可以重复使用。我们用的时候,可以直接用引用即可。

    排序条件orderBys:主要

  • 1.2.3、浅浅解释一下

    • DynamicQueryCondition类

      这个类主要是用来构建分页的条件(查询条件,分页参数…)

      @ToString
      public class DynamicQueryCondition implements Serializable {
      
          // private Map<String, QueryItem> conditions;
      
          /**
           * 条件
           */
          private List<ColumnQueryItem> conditions;
      
          /**
           * 排序方式
           */
          private Map<String, String> orderBys;
      
          /**
           * 页大小 , 默认值 10
           */
          private Integer pageSize = 10;
      
          /**
           * 当前页 , 默认值 1
           */
          private Integer pageNum = 1;
      
          /**
           * 鉴权SQL
           */
          private String authSql;
      
    • ColumnQueryItem类

      这个类主要是构建具体查询列的查询条件

      @ToString
          static class ColumnQueryItem {
      
              /**
               * 列名
               */
              private String columnName;
      
              /**
               * 查询项
               */
              private QueryItem queryItem;
      
    • QueryItem类

      这个算是比较核心的类了,他是构建条件的主要类,包括连接词,比较运算符,和查询值。

      @ToString
          static class QueryItem implements Serializable {
      
              /**
               * 查询连接词,默认AND
               */
              private String conjunction = "AND";
      
              /**
               * 操作符,如"like"、"eq"、"ne"、"gt"、"lt"、"ge"、"le"、"in"、"not in"等
               */
              private String operator;
      
              /**
               * 查询字段值
               */
              private Object value;
      
1.3、conditions使用说明:
  • 1.3.1、请求方式为Post,使用json的形式传递参数给后端

  • 1.3.2、参数说明:

    1. pageNum:与之前用法一样

    2. pageSize:同上

    3. conditions:这个为自定义的查询条件,与之前的filter用法类似。conditions为数组类型,可传多个参数,也可为空。

      • 使用示例:

      例如我们想查询表中创建人为吴彦祖条件的分页数据:

      之前filter字符串的用法:‘filter = create_user_name eq ‘吴彦祖’’,一般来说,不建议http请求url中出现中文。

      现在的的用法:

      {
          "pageNum": 1, // 当前页
          "pageSize": 10, // 页大小
          "conditions": [
              {
                  "columnName":"create_user_name",
                  "queryItem": {
                      "conjunction": "AND",
                      "operator":"eq", 
                      "value":"吴彦祖"
                  }
              }
          ]
      }
      
      • operator可用参数说明:

        1. eq:等于( == ),查询等于的情况下:比如查询金额等于xxx的记录。

          {
              "pageNum": 1, // 当前页
              "pageSize": 10, // 页大小
              "conditions": [
                  {
                      "columnName":"amount",
                      "queryItem": {
                          "conjunction": "AND", // AND可不传
                          "operator":"eq", 
                          "value":xxx
                      }
                  }
              ]
          }
                                   
          // 查询amount字段等于xxx的记录
          
        2. ne:不等于( != ),查询不等于的情况:比如查询金额不等于xxx的记录。

          {
              "pageNum": 1, // 当前页
              "pageSize": 10, // 页大小
              "conditions": [
                  {
                      "columnName":"amount",
                      "queryItem": {
                          "conjunction": "AND", // AND可不传
                          "operator":"ne", 
                          "value":xxx
                      }
                  }
              ]
          }
                                   
          // 查询amount字段不等于xxx的记录
          
        3. lt:小于( < ),查询小于情况下使用:查询金额小于xxx的记录。

          {
              "pageNum": 1, // 当前页
              "pageSize": 10, // 页大小
              "conditions": [
                  {
                      "columnName":"amount",
                      "queryItem": {
                          "conjunction": "AND", // AND可不传
                          "operator":"lt", 
                          "value":xxx
                      }
                  }
              ]
          }
                                   
          // 查询amount字段小于xxx的记录
          
        4. le:小于等于( <= ),查询小于等于情况下使用:查询金额小于等于xxx的记录。

          {
              "pageNum": 1, // 当前页
              "pageSize": 10, // 页大小
              "conditions": [
                  {
                      "columnName":"amount",
                      "queryItem": {
                          "conjunction": "AND", // AND可不传
                          "operator":"le", 
                          "value":xxx
                      }
                  }
              ]
          }
                                   
          // 查询amount字段小于等于xxx的记录
          
        5. gt:大于( > ),查询大于情况下使用:查询金额大于xxx的记录。

          {
              "pageNum": 1, // 当前页
              "pageSize": 10, // 页大小
              "conditions": [
                  {
                      "columnName":"amount",
                      "queryItem": {
                          "conjunction": "AND", // AND可不传
                          "operator":"gt", 
                          "value":xxx
                      }
                  }
              ]
          }
                                   
          // 查询amount字段大于xxx的记录
          
        6. ge:大于等于( >= ),查询大于等于情况下使用:查询金额大于等于xxx的记录。

          {
              "pageNum": 1, // 当前页
              "pageSize": 10, // 页大小
              "conditions": [
                  {
                      "columnName":"amount",
                      "queryItem": {
                          "conjunction": "AND", // AND可不传
                          "operator":"ge", 
                          "value":xxx
                      }
                  }
              ]
          }
                                   
          // 查询amount字段大于等于xxx的记录
          
        7. in:小于等于( in ),查询包含的情况下使用:查询金额是1111,2222,3333的记录。

          {
              "pageNum": 1, // 当前页
              "pageSize": 10, // 页大小
              "conditions": [
                  {
                      "columnName":"amount",
                      "queryItem": {
                          "conjunction": "AND", // AND可不传
                          "operator":"in", 
                          "value":[
                              1111,
                              2222,
                              3333
                          ] // not_in 和 in的情况下"value"值为数组类型
                      }
                  }
              ]
          }
                                   
          // 查询amount字段为:1111、2222、3333的记录
          
        8. not_in:小于等于( not in),查询不包含的情况下使用:查询金额不是1111,2222,3333的记录。

          {
              "pageNum": 1, // 当前页
              "pageSize": 10, // 页大小
              "conditions": [
                  {
                      "columnName":"amount",
                      "queryItem": {
                          "conjunction": "AND", // AND可不传
                          "operator":"not_in", 
                          "value":[
                              1111,
                              2222,
                              3333
                          ] // not_in 和 in的情况下"value"值为数组类型
                      }
                  }
              ]
          }
                                   
          // 查询amount字段不为:1111、2222、3333的记录
          
        9. like:模糊查询,情况示例:查询姓名中有‘一’的记录。

          {
              "pageNum": 1, // 当前页
              "pageSize": 10, // 页大小
              "conditions": [
                  {
                      "columnName":"create_user_name",
                      "queryItem": {
                          "conjunction": "AND", // AND可不传
                          "operator":"like", 
                          "value":"一"
                      }
                  }
              ]
          }
                                   
          // "一一一"✔
          // "一二三"✔
          // "三二一"✔
          // "三一二"✔
          
        10. like_right:模糊查询,情况示例:查询姓名中以‘一’开头的记录。

          {
              "pageNum": 1, // 当前页
              "pageSize": 10, // 页大小
              "conditions": [
                  {
                      "columnName":"create_user_name",
                      "queryItem": {
                          "conjunction": "AND", // AND可不传
                          "operator":"like_right", 
                          "value":"一"
                      }
                  }
              ]
          }
                                        
          // "一一一"✔
          // "一二三"✔
          // "三二一"✖
          
        11. like_left:模糊查询,情况示例:查询姓名中以‘一’结尾的记录。

          {
              "pageNum": 1, // 当前页
              "pageSize": 10, // 页大小
              "conditions": [
                  {
                      "columnName":"create_user_name",
                      "queryItem": {
                          "conjunction": "AND", // AND可不传
                          "operator":"like_left", 
                          "value":"一"
                      }
                  }
              ]
          }
                                        
          // "一一一"✔
          // "一二三"✖
          // "三二一"✔
          
      • conjunction可用参数说明:

        1. AND:默认值,做AND连接,示例:查询姓名为吴彦祖 并且金额大于1000的数据。

          {
              "pageNum": 1, // 当前页
              "pageSize": 10, // 页大小
              "conditions": [
                  {
                      "columnName":"create_user_name",
                      "queryItem": {
                          "conjunction": "AND", // AND可不传
                          "operator":"eq", 
                          "value":"吴彦祖"
                      },
                      "columnName":"amount",
                      "queryItem": {
                          "conjunction": "AND", // AND可不传
                          "operator":"gt", 
                          "value":1000
                      }
                  }
              ]
          }
          
        2. OR:做OR连接,示例:查询姓名为吴彦祖或者金额大于1000的金额。

          {
              "pageNum": 1, // 当前页
              "pageSize": 10, // 页大小
              "conditions": [
                  {
                      "columnName":"create_user_name",
                      "queryItem": {
                          "conjunction": "AND", // AND可不传
                          "operator":"eq", 
                          "value":"吴彦祖"
                      },
                      "columnName":"amount",
                      "queryItem": {
                          "conjunction": "OR",
                          "operator":"gt", 
                          "value":1000
                      }
                  }
              ]
          }
          
1.4、排序条件orderBys使用说明:
{
    "orderBys": {
        "CREATE_TIME": "DESC,",
        "UPDATE_TIME": "DESC"
    } // 排序条件()
}
  • 1.4.1、非必传(不传时默认按照该功能表创建时间降序排序)

  • 1.4.2、‘CREATE_TIME’列名,‘DESC’ 排序方式

  • 1.4.3、排序方式

    • 1.4.3.1 ‘ASC’ 升序排序。

      {
          "orderBys": {
              "CREATE_TIME": "ASC,",
              "UPDATE_TIME": "DESC"
          } // 排序条件()
      }
      

      按照创建时间升序排序,创建时间一致时,再按照更新时间降序排序

    • 1.4.3.2 ‘DESC’ 降序排序

      {
          "orderBys": {
              "UPDATE_TIME": "DESC"
          } // 排序条件()
      }
      

      按照更新时间降序排序

    • 1.4.3.3 注意事项:

      • 当出现多个字段排序时:

        {
            "orderBys": {
                "CREATE_TIME": "ASC,",
                "UPDATE_TIME": "DESC,",
                "xxx1":"asc,",
                "xxx2":"desc"
            } // 排序条件()
        }
        

        只有最后一个需要排序的字段排序方式后不要加 ’ , ’ ,其他在它前面的所有字段排序方式后都要加 ’ , ',

        上述例子中:xxx2字段的desc后面不用加逗号’ , ‘,在xxx2字段之前的所有要参与排序的字段,排序方式的后面都要加’ , '

1.5、 测试一下吧
  • 和平常写post请求的方法一样,只不过现在所有post请求的查询方法,都可以只要用这一个类就可以了

    • 平常我写代码,有个习惯,就是先从controller开始写,然后写到service,mapper,xml,所以先来看看controller的代码。

      /**
           * 分页查询
           *
           * @param condition 条件
           * @return
           */
          @PostMapping("/inquirePaginatedData")
          public R inquirePaginatedData(@RequestBody DynamicQueryCondition condition) {
      
              log.info(LogConstant.CALL_INTERFACE, "/visa/change-visa-approval/inquirePaginatedData");
              return service.inquirePaginatedData(condition);
      
          }
      
      • 每一个controller的接口第一句基本上都是打印日志,打印的内容就是接口地址,这样看日志定位接口比较方便。
    • service接口

          /**
           * 分页查询
           *
           * @param condition 条件
           * @return
           */
          R inquirePaginatedData(DynamicQueryCondition condition);
      
    • serviceImpl实现类

      /**
           * 分页查询
           *
           * @param condition 条件
           * @return
           */
          @Override
          public R inquirePaginatedData(DynamicQueryCondition condition) {
      
              try {
                  PageHelper.startPage(condition.getPageNum(), condition.getPageSize(), true);
                  Page<ChangeVisaApproval> page = baseMapper.selectPaginatedData(condition);
                  return R.success(ResultRspDTO.buildResponseData(page), SusEnum.SELECTED);
              } catch (Exception e) {
                  e.printStackTrace();
                  log.error(LogConstant.QUERY_FAIL, e.getMessage());
                  return R.failed(FailEnum.SELECT_FAILED, e);
              }
          }
      
      • 我个人的习惯就是写分页接口,都是喜欢用pageHelper做分页,不知道大家都用什么,感觉用着很方便,查询之前写个语句即可,最后一个参数true,是你想不想要统计本次查询的总数,true需要,false不需要,如果false的话,就只会有分页的数据,总数的话就没有了,所以一般来说,分页都是需要总数的,一般都会是true,统计总数。

      • 第一行代码就是具体的查询语句了,待会再看。

      • 第三行就是直接返回,其中ResultRspDTO.buildResponseData(page)这句话就是构建统一的查询结果返回给前端。可以看看代码。如果需要的话也可以用。

        /**
         * 通用查询响应类, 无需新建更多的RspDTO类
         * @author: 一位大帅哥
         * @date: 2022/11/11 11:25
         * @description: TODO
         */
        @Data
        public class ResultRspDTO<T> {
          
            /**
             * 查询结果
             */
            private T list;
          
            /**
             * 总记录数
             */
            private Long total;
          
            /**
             * 页大小
             */
            private Integer pageSize;
          
            /**
             * 当前页
             */
            private Integer pageNum;
          
            /**
             * 总页数
             */
            private Integer pages;
          
            /**
             * 构建返回类的参数
             * @param page pageHelper查询结果
             * @return 组装完成的类
             */
            public static ResultRspDTO buildResponseData(Page page) {
                ResultRspDTO rspDTO = new ResultRspDTO();
                rspDTO.setList(page.getResult());
                rspDTO.setPages(page.getPages());
                rspDTO.setTotal(page.getTotal());
                rspDTO.setPageNum(page.getPageNum());
                rspDTO.setPageSize(page.getPageSize());
                return rspDTO;
            }
          
            /**
             * 构建查询为空的返回类参数
             * @param reqDTO 入参对象
             * @return 组装完成的类
             */
            public static ResultRspDTO buildResponseData(PageReqDTO reqDTO) {
          
                ResultRspDTO rspDTO = new ResultRspDTO();
                rspDTO.setList(Collections.emptyList());
                rspDTO.setPages(0);
                rspDTO.setTotal(0L);
                rspDTO.setPageNum(reqDTO.getPageNum());
                rspDTO.setPageSize(reqDTO.getPageSize());
                return rspDTO;
            }
        }
        

        其实也就是用了一个泛型,然后把查询PageHerlper的Page对象的属性设置到这个统一的类中。

    • mapper接口

          /**
           * 分页查询
           *
           * @param condition 条件
           * @return
           */
          Page<ChangeVisaApproval> selectPaginatedData(DynamicQueryCondition condition);
      
    • mapper对应的xml

          <select id="selectPaginatedData" resultType="com.jpmsoft.visa.entity.ChangeVisaApproval">
              SELECT
                  mn.*
              FROM
                  change_visa_approval mn
                      <include refid="com.jpmsoft.single.sys.mapper.PreProjectMapper.Auth_Sql"/>
              <include refid="com.jpmsoft.single.sys.mapper.PreProjectMapper.Base_DynamicQueryCondition_Sql"/>
      <include refid="com.jpmsoft.single.sys.mapper.PreProjectMapper.Base_DynamicQueryOrderBy_Sql"/>
          </select>
      

      Auth_Sql这个片段是我们这边业务特有的,是一个连表查询的一个Sql片段。

      如果只查询单表的东西,只需要返回mn.* 即可,有连表查询的直接写在后面即可,主要查的表一定要mn的别名。

    • 接口测试一下

      入参

      {
          "pageNum": 1,
          "pageSize": 10,
          "conditions": [
              {
                  "columnName": "CREATE_USER_NAME",
                  "queryItem": {
                      "conjunction":"AND",
                      "operator": "eq",
                      "value": "庄小军"
                  }
              }
          ],
          "orderBys": {
              "CREATE_TIME": "desc"
          }
      }
      

      这个意思呢,就是查询这个主表的创建人是庄小军的记录,第一页,每页10条,并按照创建时间排序,我们来看看执行的日志sql。

      SELECT mn.* FROM change_visa_approval mn left join Pre_Project fpro on fpro.P_Id=mn.PROJECT_ID where 1=1 AND mn.CREATE_USER_NAME = ? ORDER BY mn.CREATE_TIME desc LIMIT ?

      对应的 ? 的参数

      庄小军(String), 10(Integer),

      可以看到,查询的条件庄小军对应的是第一个? 正确,第一个则是分页的大小。

    • 我们再来测试一个稍微复杂一点的。

      {
          "pageNum": 1,
          "pageSize": 10,
          "conditions": [
              {
                  "columnName": "CREATE_USER_NAME",
                  "queryItem": {
                      "conjunction":"AND",
                      "operator": "eq",
                      "value": "庄小军"
                  }
              },
              {
                  "columnName": "TITLE",
                  "queryItem": {
                      "conjunction":"OR",
                      "operator": "LIKE",
                      "value": "俱乐部往外走道原有三樘门改造"
                  }
              }
          ],
          "orderBys": {
              "BASIS_DOCUMENT": "ASC,", // 逗号一定要加,不然拼接的sql会语法错误,因为前面的缺少, 前面不加逗号,会拼成这样ORDER BY mn.BASIS_DOCUMENT ASC mn.CREATE_TIME DESC,ASC后缺少逗号(,)
              "CREATE_TIME": "DESC"
          }
      }
      

      这个就是比刚刚多了一个条件,是or连接,并且是like的模糊查询。排序条件:先按照BASIS_DOCUMENT升序排序,再按照

      CREATE_TIME降序排序。

      来看看后台打印的sql

      SELECT mn.* FROM change_visa_approval mn left join Pre_Project fpro on fpro.P_Id=mn.PROJECT_ID where 1=1 AND mn.CREATE_USER_NAME = ? OR mn.TITLE LIKE CONCAT(‘%’, ?, ‘%’) ORDER BY mn.BASIS_DOCUMENT ASC, mn.CREATE_TIME DESC LIMIT ?

      对应的入参:

      庄小军(String), 俱乐部往外走道原有三樘门改造(String), 10(Integer)

      可以看到,我们的模糊查询也是生效的,拼接出来的or连接条件也是正确的

1.6、不足之处及初步解决办法(可不看,目前所有字段都是mn.开头,如果后面需要查询其他的表,则该方法不满足,需要另写)
  • 1.6.1、前端同事如果按照我上面的要求调用接口查询出现查询失败,或者接口返回500的情况是,很有可能是后端拼接的sql有问题。

    • 解决办法:

      因为后端sql语句有很多都是做了多表查询这个操作,所以,你查询的列,可能和连接表里的列名一样,前端直接传的列字段名,没有加对应的别名就导致了数据库不知道用哪个表的这个字段进行查询。

      这种情况,可以试着在列字段名前加:mn.字段名,mn即这个功能的表,如果你要查询的字段,这个功能的表有这个字段(新增页面,有这个字段,或者一些公共字段(创建时间,流程状态之类的)),就可以用这种方法,给字段名前加:mn.

  • 1.6.2、有用到的秃友门,拿到之后可以改进,有啥不足的地方自行补充,也可以提交到我的git上来,嘿嘿,大家一起改进,一起嗨。

    gitee: https://gitee.com/gaoming_1998/DynamicQueryCondition_code.git

​ github: 暂无

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值