一个多功能(聚合)查询接口,实现模糊、分页、主键、排序以及多条件查询

news2024/11/18 17:28:01

一个多功能(聚合)查询接口,实现模糊、分页、主键、排序以及多条件查询

前言

写的啰嗦了点,看效果请直接忽略中间,直接看后半部分。

引个流,公众号:小简聊开发

概念

瞎编的名字,哈哈哈,我就勉强称之为聚合查询吧,不知道概念符不符合。

大家好,我是小简,很久没写文章了,确实是太忙了,今天我异想天开(其实也有很多类似的实现,只不过没去封装)的想去实现一个查询接口,不同条件不同查询的功能,简单的玩玩,如果大家有更好的思路或者见解,可以评论区互动一下,我很菜,还请理解,大佬勿喷。

在日常开发中,我们基本上是使用RESTful接口,也就是一个接口对应一个功能,这很方便前端开发的对接,具体优势我想我就不必说了,大家都在使用。

但是RESTful如果功能过多,对应的接口也会随之增多,比如后台的查询接口和前台可能数据有区别需要额外写一个,可能前台查询数据某一个要模糊查询,某一个又要走主键查询,有的又是多条件查询。

那我就在想,要不试试一个查询接口,聚合N个实现?不同数据去不同实现然后不同拼合。

那其实这时候,GraphQL这个玩意,其实就很符合我的预想,这玩意的接口查询就是一个接口实现的,大概意思和大致想法都是一样的。

但是GraphQL的开发和使用复杂度要高一些,而且我还是喜欢RESTful一些,这里不做过多介绍与对比,有兴趣可以前往浏览器搜索一些,了解了解。

预想

那该怎么去实现呢?我的项目使用的是MyBatisPlus作为ORM框架,每一个实体都是去映射数据库表的,我要想这一个功能方便通用,那我肯定要去进行部分封装的。

首先考虑一下目前项目理想状态:

  1. 实体映射数据库
  2. 数据库字段规范的采用蛇形命名,无一例外
  3. 实体类采用驼峰命名映射,无一例外

这种情况下,我只需要通过反射,将一个传过来的实体类的字段从驼峰转换为蛇形命名,作为查询条件的Key,也就是数据库字段名。

然后获取对应的值,作为条件拼接,然后将查询条件对象丢到MyBatisPlusservice里面就可以了。

那么…开干!

查询条件拼接

我们MP是使用QueryWrapper来构建条件查询对象的,总共有四种情况:

  1. 基本查询条件:queryWrapper.eq()
  2. 模糊查询条件:queryWrapper.like()
  3. 排序查询条件:queryWrapper.orderByXXX()

至于主键查询,我预想是创建一个注解作为标识,如果在逻辑处理时候发现主键标识注解的字段值不为空,我就直接抛弃所有其他条件,直接走主键查询,ById

但是因为时间原因,我就没去具体实现了。

获取对象字段与值

  1. 先通过反射获取对象所有属性并遍历。

  2. 如果属性值不为null就拼接。

  3. 使用Hutool中的StrUtil.toUnderlineCase将属性名称转换蛇形。

拼接条件

代码如下:

    /**
     * 聚合查询对象拼接
     *
     * @param queries 查询对象
     * @param obj     聚合查询属性对象
     * @return 查询对象
     */
    public static <Q> QueryWrapper<Q> splicingQueries(QueryWrapper<Q> queries, Object obj) {
        Field[] declaredFields = obj.getClass().getDeclaredFields();
        for (Field field : declaredFields) {
            field.setAccessible(true);
            String underlineCase = StrUtil.toUnderlineCase(field.getName());
            try {
                if (field.get(obj) != null) {
                    queries.eq(underlineCase, field.get(obj));
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return queries;
    }

大概代码就是这样的,然后我给了个泛型,为了约束查询对象的类型。

那有人就说了:“那这样,模糊查询怎么办?排序怎么办?分页怎么办?”

哎哎哎,别急嘛,且听我娓娓道来。

拼接模糊

同理,去实现这个模糊查询的拼接方法封装。

    /**
     * 模糊查询对象拼接
     *
     * @param queries 查询对象
     * @param obj     模糊查询属性对象
     * @return 查询对象
     */
    public static <Q> QueryWrapper<Q> splicingFuzzyQueries(QueryWrapper<Q> queries, Object obj) {
        Field[] declaredFields = obj.getClass().getDeclaredFields();
        for (Field field : declaredFields) {
            field.setAccessible(true);
            String underlineCase = StrUtil.toUnderlineCase(field.getName());
            try {
                if (field.get(obj) != null) {
                    queries.like(underlineCase, field.get(obj));
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return queries;
    }

拼接排序

    /**
     * 排序
     *
     * @param wrapper   查询对象
     * @param sortField 排序字段
     * @param sortType  排序类型
     */
    private static <Q> void applySort(QueryWrapper<Q> wrapper, String sortField, int sortType) {
        String field = StrUtil.toUnderlineCase(sortField);
        if (sortType == 1) {
            wrapper.orderByDesc(field);
        } else {
            wrapper.orderByAsc(field);
        }
    }

这里其实可以优化sortType为枚举,不过…嘿嘿,我比较懒,算了算了吧。

分页实现

分页先留在后面了,客官先往下看。

逻辑处理

到目前,功能的方法封装好了,整理一下业务的逻辑。

在排除主键(没时间写啦)查询的情况下,逻辑总结如下:

  1. 如果存在绝对查询条件情况,如:ID查询、手机号查询、邮箱查询,直接性查询,就不拼接模糊查询了

  2. 然后,基本查询条件,可以与绝对条件查询互补,也可以与模糊查询条件互补

  3. 对于模糊查询,如果没有绝对查询条件的情况下,才会去拼接,模糊可与基本条件互补

  4. 对于排序条件,只要数据存在就需要拼接,并且如果排序方式为null,默认升序。

  5. 对于分页条件,我设置的是必传项,并且没去设置默认值(懒…)。

那将这个逻辑串联起来,就可以实现一个通用方法。

但是!!!

在实现这个方法之前,我如何区分数据是要模糊还是基本条件或者又是绝对查询条件?

所以,我们需要先封装一个实体类,这个实体我们可能每一个对象都有自己不一样的属性,所以我们不能写死,直接泛化即可。

实体封装

先看我的注释,截个图:

再贴所有的代码:

/*
 * Copyright (c) 2023. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan.
 * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna.
 * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus.
 * Vestibulum commodo. Ut rhoncus gravida arcu.
 * © JanYork 2023年6月2日 23点37分
 */
 
/**
 * <p>
 * 泛型说明:
 *     <ul>
 *         <li>T:直接性查询条件对象</li>
 *         <li>C:基本查询条件对象</li>
 *         <li>R:模糊查询条件对象</li>
 *         <li>泛型对象可以为空,为空时不进行查询</li>
 *         <li>泛型对象必须是一个Bean</li>
 *         <li>泛型对象的属性<b>必须是基本数据类型</b></li>
 *     </ul>
 * </p>
 *
 * @author JanYork
 * @version 1.0.0
 * @date 2023/06/02
 * @description 聚合查询对象
 * @since 1.0.0
 */
@Data
@ApiModel(value = "聚合查询对象")
public class AggregateQueries<T, C, R> {
    /**
     * 直接性查询条件对象(T是一个Bean)
     * <p>
     * 直接性查询对象如果存在,<b>模块查询条件直接失效</b>,场景如:<br/> <b>ID直接查询</b>、<b>手机号直接查询</b>
     * </p>
     */
    @ApiModelProperty(value = "直接性查询条件对象")
    private T queries;
    /**
     * 分页信息对象
     * <p>
     * 分页对象包含分页信息,<b>分页信息必须存在</b>,场景如:<br/> <b>分页查询</b>
     * </p>
     */
    @ApiModelProperty(value = "分页信息对象")
    private PaginationDTO pagination;
    /**
     * 基本查询条件对象(C是一个Bean)
     * <p>
     * 基本查询对象与直接性查询可以同时存在,<b>基本查询条件对象的查询条件会与直接性查询条件对象的查询条件进行组合</b>,场景如:<br/>
     * <b>直接性查询ID为10001的用户</b>,<b>基本性查询状态为true的用户</b>,结合后的查询条件为:<br/>
     * <b>查询ID为10001且状态为true的用户</b>
     * </p>
     */
    @ApiModelProperty(value = "基本查询条件对象")
    private C condition;
    /**
     * 模糊查询条件对象(R是一个Bean)
     * <p>
     * 模糊查询与直接性条件查询互斥,与基本查询条件对象互补,<b>模糊查询条件对象的查询条件会与基本查询条件对象的查询条件进行组合</b>,场景如:<br/>
     * <b>基本性查询状态为true的用户</b>,<b>模糊性查询用户名为张三的用户</b>,结合后的查询条件为:<br/>
     * <b>查询状态为true且用户名包含张三的用户</b>
     * </p>
     */
    @ApiModelProperty(value = "模糊查询条件对象")
    private R fuzzyQueries;
    /**
     * 排序字段
     * <p>
     * 排序字段可以为空,为空时不进行排序
     * </p>
     */
    @ApiModelProperty(value = "排序字段")
    private String sortField;
    /**
     * 排序方式
     * <p>
     * 排序方式可以为空,为空时默认为升序,0:升序,1:降序
     * </p>
     */
    @ApiModelProperty(value = "排序方式")
    private Integer sortType;

    /**
     * 是否存在直接性查询条件对象
     *
     * @return boolean true:存在,false:不存在
     */
    public boolean hasQueries() {
        return queries != null;
    }

    /**
     * 是否存在分页信息对象
     *
     * @return boolean true:存在,false:不存在
     */
    public boolean hasPagination() {
        return pagination != null;
    }

    /**
     * 是否存在基本查询条件对象
     *
     * @return boolean true:存在,false:不存在
     */
    public boolean hasCondition() {
        return condition != null;
    }

    /**
     * 是否存在模糊查询条件对象
     *
     * @return boolean true:存在,false:不存在
     */
    public boolean hasFuzzyQueries() {
        return fuzzyQueries != null;
    }

    /**
     * 是否存在排序字段
     *
     * @return boolean true:存在,false:不存在
     */
    public boolean hasSortField() {
        return sortField != null;
    }

    /**
     * 是否存在排序方式
     *
     * @return boolean true:存在,false:不存在
     */
    public boolean hasSortType() {
        return sortType != null;
    }
}

这里面有一个PaginationDTO,是一个分页信息对象,我也贴出来:

/**
 * @author JanYork
 * @version 1.0.0
 * @date 2023/06/02
 * @description 分页DTO
 * @since 1.0.0
 */
@Data
@ApiModel(value = "分页数据对象")
public class PaginationDTO {
    /**
     * 页码
     */
    @ApiModelProperty(value = "页码")
    private Integer page;
    /**
     * 每页大小
     */
    @ApiModelProperty(value = "每页大小")
    private Integer size;
}

那这样我们就封装成实体类了。

三个泛型?

三个泛型对象其实我在注释中已经说了:

泛型说明:

  1. T:直接性查询条件对象
  2. C:基本查询条件对象
  3. R:模糊查询条件对象
  4. 泛型对象可以为空,为空时不进行查询(不拼接)
  5. 泛型对象必须是一个Bean(实体),并且实体必须映射数据表,字段必须规范命名
  6. 泛型对象的属性必须是基本数据类型

然后对于这三个泛型,我们要创建不同的实体(麻烦归麻烦,规范还是要),所以,接下来看看我怎么用的。

我这里一个地址聚合查询和一个商品聚合查询。

构建实体?

Condition结尾的是基本查询条件对象,如:

/**
 * @author JanYork
 * @version 1.0.0
 * @date 2023/06/02
 * @description 地址基本条件查询对象
 * @since 1.0.0
 */
@Data
@ApiModel("(地址)基本条件查询对象")
public class AddressCondition {
    /**
     * 省
     */
    @ApiModelProperty(value = "省")
    private String province;
    /**
     * 城市
     */
    @ApiModelProperty(value = "城市")
    private String city;
    /**
     * 县/区
     */
    @ApiModelProperty(value = "县/区")
    private String district;
}

这里面的条件,如果存在,那就必定会拼接,与绝对查询条件或者模糊查询条件互补

FuzzyQueries结尾的是模糊查询条件,如:

/**
 * @author JanYork
 * @version 1.0.0
 * @date 2023/06/02
 * @description 地址模糊查询对象
 * @since 1.0.0
 */
@Data
@ApiModel("(地址)模糊查询对象")
public class AddressFuzzyQueries {
    /**
     * 用户手机号
     */
    @ApiModelProperty(value = "用户手机号(可模糊)")
    private String phone;
    /**
     * 用户姓名
     */
    @ApiModelProperty(value = "用户姓名(可模糊)")
    private String name;
    /**
     * 详细地址
     */
    @ApiModelProperty(value = "详细地址(可模糊)")
    private String address;
}

这个实体里面的条件与绝对查询条件互斥,且优先级低于绝对查询条件

Queries结尾的是绝对查询条件,如:

/**
 * @author JanYork
 * @version 1.0.0
 * @date 2023/06/02
 * @description 地址直接性查询对象
 * @since 1.0.0
 */
@Data
@ApiModel("(地址)直接性查询对象")
public class AddressQueries {
    /**
     * 用户ID
     */
    @ApiModelProperty(value = "用户ID")
    private Long userId;
    /**
     * 用户手机号
     */
    @ApiModelProperty(value = "用户手机号")
    private String phone;
}

这个实体条件优先级最高,如果存在必定拼接。

然后三个实体都有了,我们还需要将上面逻辑处理段落总结的逻辑串联起来,实现一个通用方法。

通用方法实现

我们开始已经封装了模糊、条件、排序的拼接方法,所以我们可以直接调用封装好的方法去实现拼接的逻辑处理。

直接丢代码了:

    /**
     * 聚合查询对象拼接
     *
     * @param queries   查询对象
     * @param aggregate 聚合查询对象
     * @return {@link QueryWrapper}<{@link Q}>
     */
    public static <Q, T, C, R> QueryWrapper<Q> splicingAggregateQueries(QueryWrapper<Q> queries, AggregateQueries<T, C, R> aggregate) {
        if (aggregate.hasQueries()) {
            splicingQueries(queries, aggregate.getQueries());
        }
        if (aggregate.hasCondition()) {
            splicingQueries(queries, aggregate.getCondition());
        }
        if (aggregate.hasFuzzyQueries() && !aggregate.hasQueries()) {
            splicingFuzzyQueries(queries, aggregate.getFuzzyQueries());
        }
        if (aggregate.hasSortField()) {
            aggregate.setSortType(aggregate.hasSortType() ? aggregate.getSortType() : 0);
            applySort(queries, aggregate.getSortField(), aggregate.getSortType());
        }
        return queries;
    }

这个通用方法,主要就是实现通用性和逻辑贯穿处理,这里没写过多注释,aggregate.hasXXXX方法是判断是否存在条件实体对象或者字段值,逻辑还需要请各位亲自捋一下了,毕竟也就几个if

使用聚合方法与分页操作

先看代码后讲解:

    /**
     * 聚合查询
     *
     * @param aggregate 聚合查询对象
     * @return {@link ApiResponse}<{@link List}<{@link Address}>>
     */
    @PostMapping("/get")
    public ApiResponse<List<Address>> get(@RequestBody AggregateQueries<AddressQueries, AddressCondition, AddressFuzzyQueries> aggregate) {
        if (!aggregate.hasPagination()) {
            return ApiResponse.fail(null);
        }
        PaginationDTO pagination = aggregate.getPagination();
        QueryWrapper<Address> wrapper = AggregateQueriesUtil.splicingAggregateQueries(new QueryWrapper<>(), aggregate);
        Page<Address> page = new Page<>(pagination.getPage(), pagination.getSize());
        return ApiResponse.ok(
                addressService.page(page, wrapper).getRecords()
        );
    }

这里,接口接收的参数就是映射我们封装的聚合条件对象:AggregateQueries<AddressQueries, AddressCondition, AddressFuzzyQueries> aggregate了。

这三个泛型就是我们对于不同数据库实体的不同条件字段封装的不同POJO

分页

先前说了,分页是必须的,所以我们判断一下,分页对象不存在直接返回失败。

if (!aggregate.hasPagination()) {
    return ApiResponse.fail(null);
}

如果存在我们获取分页信息,利用MP的分页插件,直接进行分页查询操作,这个插件就不多说了。

处理

这里我们直接利用我们封装好的通用方法去获取拼接条件后的QueryWrapper对象:

QueryWrapper<Address> wrapper = AggregateQueriesUtil.splicingAggregateQueries(new QueryWrapper<>(), aggregate);

因为是后台管理端的数据,我就懒得去Bean拷贝到VO对象了,直接就是实体返回。

效果演示

调试工具用什么好呢?上postman吧。

数据库数据展示

1000 1658741489930473472 江西省 南昌市 南昌县 江西省南昌市南昌县万寿宫 16688880818 小简 28.68333 115.88333 0

1001 1658741489930473472 江西省 南市 南昌县 江西省南昌市南昌县东湖区ABCD6栋 16670080818 小简 28.68333 115.88333 0

1002 1658741489930473472 江西省 昌市 南昌县 江西省南昌市南昌县CCC写字楼 16676080818 小简 28.68333 115.88333 1

测试JSON数据:

{
  "queries": {
      "phone":"16688880818"
  },
  "pagination": {
    "page": 1,
    "size": 3
  },
  "condition":{
      "city":"南市"
  },
  "fuzzyQueries": {
      "phone": "166",
      "address": "CCC写字楼"
  },
  "sortField": "id",
  "sortType": 1
}

绝对条件查询

数据:

{
  "queries": {
      "phone":"16688880818"
  },
  "pagination": {
    "page": 1,
    "size": 3
  }
}

SQL日志:

SELECT id,user_id,province,city,district,address,phone,name,latitude,longitude,is_default FROM address WHERE (phone = ?) LIMIT ?

MP完整日志:

Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@164276c] was not registered for synchronization because synchronization is not active
JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@71237c20] will not be managed by Spring
==>  Preparing: SELECT COUNT(*) AS total FROM address WHERE (phone = ?)
==> Parameters: 16688880818(String)
<==    Columns: total
<==        Row: 1
<==      Total: 1
==>  Preparing: SELECT id,user_id,province,city,district,address,phone,name,latitude,longitude,is_default FROM address WHERE (phone = ?) LIMIT ?
==> Parameters: 16688880818(String), 3(Long)
<==    Columns: id, user_id, province, city, district, address, phone, name, latitude, longitude, is_default
<==        Row: 1000, 1658741489930473472, 江西省, 南昌市, 南昌县, 江西省南昌市南昌县万寿宫, 16688880818, 小简, 28.68333, 115.88333, 0
<==      Total: 1

回调数据:

{
    "code": 200,
    "message": "success",
    "data": [
        {
            "id": 1000,
            "userId": 1658741489930473472,
            "province": "江西省",
            "city": "南昌市",
            "district": "南昌县",
            "address": "江西省南昌市南昌县万寿宫",
            "phone": "16688880818",
            "name": "小简",
            "latitude": "28.68333",
            "longitude": "115.88333",
            "isDefault": false
        }
    ]
}

绝对+基本

数据:

{
  "queries": {
      "phone":"16688880818"
  },
  "pagination": {
    "page": 1,
    "size": 3
  },
  "condition":{
      "city":"南昌市"
  }
}

SQL日志:

SELECT id,user_id,province,city,district,address,phone,name,latitude,longitude,is_default FROM address WHERE (phone = ? AND city = ?) LIMIT ?

MP日志:

Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2e00c6c9] was not registered for synchronization because synchronization is not active
JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@5e68f14e] will not be managed by Spring
==>  Preparing: SELECT COUNT(*) AS total FROM address WHERE (phone = ? AND city = ?)
==> Parameters: 16688880818(String), 南昌市(String)
<==    Columns: total
<==        Row: 1
<==      Total: 1
==>  Preparing: SELECT id,user_id,province,city,district,address,phone,name,latitude,longitude,is_default FROM address WHERE (phone = ? AND city = ?) LIMIT ?
==> Parameters: 16688880818(String), 南昌市(String), 3(Long)
<==    Columns: id, user_id, province, city, district, address, phone, name, latitude, longitude, is_default
<==        Row: 1000, 1658741489930473472, 江西省, 南昌市, 南昌县, 江西省南昌市南昌县万寿宫, 16688880818, 小简, 28.68333, 115.88333, 0
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2e00c6c9]

回调数据:

{
    "code": 200,
    "message": "success",
    "data": [
        {
            "id": 1000,
            "userId": 1658741489930473472,
            "province": "江西省",
            "city": "南昌市",
            "district": "南昌县",
            "address": "江西省南昌市南昌县万寿宫",
            "phone": "16688880818",
            "name": "小简",
            "latitude": "28.68333",
            "longitude": "115.88333",
            "isDefault": false
        }
    ]
}

模糊

数据:

{
  "pagination": {
    "page": 1,
    "size": 3
  },
  "fuzzyQueries": {
      "phone": "166"
    //   "address": "CCC写字楼"
  }
}

SQL日志:

SELECT id,user_id,province,city,district,address,phone,name,latitude,longitude,is_default FROM address WHERE (phone LIKE ?) LIMIT ?

MP日志:

Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3646a7c1] was not registered for synchronization because synchronization is not active
JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@3e66a291] will not be managed by Spring
==>  Preparing: SELECT COUNT(*) AS total FROM address WHERE (phone LIKE ?)
==> Parameters: %166%(String)
<==    Columns: total
<==        Row: 3
<==      Total: 1
==>  Preparing: SELECT id,user_id,province,city,district,address,phone,name,latitude,longitude,is_default FROM address WHERE (phone LIKE ?) LIMIT ?
==> Parameters: %166%(String), 3(Long)
<==    Columns: id, user_id, province, city, district, address, phone, name, latitude, longitude, is_default
<==        Row: 1000, 1658741489930473472, 江西省, 南昌市, 南昌县, 江西省南昌市南昌县万寿宫, 16688880818, 小简, 28.68333, 115.88333, 0
<==        Row: 1001, 1658741489930473472, 江西省, 南市, 南昌县, 江西省南昌市南昌县东湖区ABCD6栋, 16670080818, 小简, 28.68333, 115.88333, 0
<==        Row: 1002, 1658741489930473472, 江西省, 昌市, 南昌县, 江西省南昌市南昌县CCC写字楼, 16676080818, 小简, 28.68333, 115.88333, 1
<==      Total: 3
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3646a7c1]

回调数据:

{
    "code": 200,
    "message": "success",
    "data": [
        {
            "id": 1000,
            "userId": 1658741489930473472,
            "province": "江西省",
            "city": "南昌市",
            "district": "南昌县",
            "address": "江西省南昌市南昌县万寿宫",
            "phone": "16688880818",
            "name": "小简",
            "latitude": "28.68333",
            "longitude": "115.88333",
            "isDefault": false
        },
        {
            "id": 1001,
            "userId": 1658741489930473472,
            "province": "江西省",
            "city": "南市",
            "district": "南昌县",
            "address": "江西省南昌市南昌县东湖区ABCD6栋",
            "phone": "16670080818",
            "name": "小简",
            "latitude": "28.68333",
            "longitude": "115.88333",
            "isDefault": false
        },
        {
            "id": 1002,
            "userId": 1658741489930473472,
            "province": "江西省",
            "city": "昌市",
            "district": "南昌县",
            "address": "江西省南昌市南昌县CCC写字楼",
            "phone": "16676080818",
            "name": "小简",
            "latitude": "28.68333",
            "longitude": "115.88333",
            "isDefault": true
        }
    ]
}

排序+模糊

数据:

{
  "pagination": {
    "page": 1,
    "size": 3
  },
  "fuzzyQueries": {
      "phone": "166"
  },
  "sortField": "id",
  "sortType": 1
}

SQL日志:

SELECT id,user_id,province,city,district,address,phone,name,latitude,longitude,is_default FROM address WHERE (phone LIKE ?) ORDER BY id DESC LIMIT ?

MP日志:

Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@26a0b246] was not registered for synchronization because synchronization is not active
JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@3deaaebe] will not be managed by Spring
==>  Preparing: SELECT COUNT(*) AS total FROM address WHERE (phone LIKE ?)
==> Parameters: %166%(String)
<==    Columns: total
<==        Row: 3
<==      Total: 1
==>  Preparing: SELECT id,user_id,province,city,district,address,phone,name,latitude,longitude,is_default FROM address WHERE (phone LIKE ?) ORDER BY id DESC LIMIT ?
==> Parameters: %166%(String), 3(Long)
<==    Columns: id, user_id, province, city, district, address, phone, name, latitude, longitude, is_default
<==        Row: 1002, 1658741489930473472, 江西省, 昌市, 南昌县, 江西省南昌市南昌县CCC写字楼, 16676080818, 小简, 28.68333, 115.88333, 1
<==        Row: 1001, 1658741489930473472, 江西省, 南市, 南昌县, 江西省南昌市南昌县东湖区ABCD6栋, 16670080818, 小简, 28.68333, 115.88333, 0
<==        Row: 1000, 1658741489930473472, 江西省, 南昌市, 南昌县, 江西省南昌市南昌县万寿宫, 16688880818, 小简, 28.68333, 115.88333, 0
<==      Total: 3
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@26a0b246]

回调数据:

{
    "code": 200,
    "message": "success",
    "data": [
        {
            "id": 1002,
            "userId": 1658741489930473472,
            "province": "江西省",
            "city": "昌市",
            "district": "南昌县",
            "address": "江西省南昌市南昌县CCC写字楼",
            "phone": "16676080818",
            "name": "小简",
            "latitude": "28.68333",
            "longitude": "115.88333",
            "isDefault": true
        },
        {
            "id": 1001,
            "userId": 1658741489930473472,
            "province": "江西省",
            "city": "南市",
            "district": "南昌县",
            "address": "江西省南昌市南昌县东湖区ABCD6栋",
            "phone": "16670080818",
            "name": "小简",
            "latitude": "28.68333",
            "longitude": "115.88333",
            "isDefault": false
        },
        {
            "id": 1000,
            "userId": 1658741489930473472,
            "province": "江西省",
            "city": "南昌市",
            "district": "南昌县",
            "address": "江西省南昌市南昌县万寿宫",
            "phone": "16688880818",
            "name": "小简",
            "latitude": "28.68333",
            "longitude": "115.88333",
            "isDefault": false
        }
    ]
}

其他效果

都可以自由组合,单排序、单分页、单模糊、排序+分页+模糊、排序+基本…

都是可以自由组合的,方法太多就不都去尝试了,大家也可以去试试,痛苦的封装也会带来一定的好处。

也可以在此基础上,去创造一些新鲜的玩法和业务实现,感谢大家阅读,下期再见啦!

我是小简,下期再会(不知道下期是多久了唉)。

尾述

为什么要去写一些七里八里的东东呢?

因为,创造性才是本质,我不希望被CRUD的工作丢失我的兴趣与活跃的思维。

我认为,赚钱的永远是思维和大脑,而非单纯的、一味的技术。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/604285.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一道北大强基题背后的故事(二)——出题者怎么想的?

早点关注我&#xff0c;精彩不错过&#xff01; 上篇文章中&#xff0c;我们给出一道北大强基考试中的试题&#xff0c;计算[((1 sqrt(5)) / 2) ^ 12]&#xff0c;给出了一条没有任何数学直觉&#xff0c;纯硬算的弯路以及题目的参考答案&#xff0c;相关内容请戳&#xff1a;…

IronPDF 2023.6.10 FOR NET CRACK

适用于.NET的IronPDF 2023.6.10 添加新的注释API并改进图像压缩逻辑。 2023年6月2日-14:42新版 特点 添加了新的连续进给选项。例如用于生成收据文档。 添加了新的注释API&#xff0c;包括注释删除。 添加了删除书签的功能。 将内存使用率和性能提高了10%。 改进了图像…

全球加速AEB「强标」,15万元以下车型或将「释放」巨量需求

目前&#xff0c;智能驾驶技术升级&#xff0c;主要是在帮助中高端车型提升产品竞争力&#xff0c;同时为车企构建未来软件付费盈利模式的转型。 但另一方面&#xff0c;基础辅助驾驶&#xff0c;尤其主动安全&#xff0c;比如&#xff0c;AEB&#xff08;自动紧急制动&#xf…

【企业化架构部署】基于Apache搭建LAMP架构

文章目录 前言一、LMAP架构介绍1.概念2.LAMP构建顺序3.LAMP编译安装4.各组件介绍4.1 Linux4.2 Apache4.3 MySQL4.4 PHP/Perl/Python 二、服务器部署1.Apache部署2.MySQL部署3.PHP部署4.安装论坛 前言 LAMP架构是目前成熟的企业网站应用模式之一&#xff0c;指的是协同工作的一整…

考上大学,走进网安

出品&#xff5c;MS08067实验室&#xff08;www.ms08067.com&#xff09; 本文作者&#xff1a;北平靴 01 我和网络安全的相遇&#xff0c;说来很巧。2022年我考入吉林大学计算机学院&#xff0c;正巧我们的导助是学校ctf战队的成员&#xff0c;所以在刚入学的很清闲的日子里&a…

Python中logger模块的使用教程

参考模块&#xff1a; https://www.digitalocean.com/community/tutorials/how-to-use-logging-in-python-3 logger是python的内置模块&#xff0c;用以输出代码运行过程中的运行情况&#xff0c;极大的方便了我们的debug过程。参考资料中列出了使用logger相比于print优越的地方…

ResNet学习

目录 是什么作用怎么做论文阅读 视频来源&#xff1a;https://www.bilibili.com/video/BV1rZ4y1m7d9/?spm_id_from333.337.search-card.all.click&vd_sourcea402747bd6c153bdb2defee02a9cb617 https://www.bilibili.com/video/BV1rZ4y1m7d9/?spm_id_from333.788&vd_…

Java官方笔记5数字和字符串

Numbers Number的子类&#xff1a; 另外还有BigDecimal和BigInteger&#xff0c;用于高精度计算&#xff0c;AtomicInteger和AtomicLong用于多线程应用。 我们有时候需要用包装类而非基本数据类型&#xff0c;理由如下&#xff1a; 方法入参类型为Object&#xff0c;只能传入对…

什么是Linux shell

一句话概括&#xff1a;简单来说脚本就是将需要执行的命令保存到文本中&#xff0c;按照顺序执行&#xff08;由上往下执行&#xff09;&#xff0c;shell脚本:shell脚本就是一些命令的集合。 一、创建第一个Shell脚本&#xff1a;输出helloworld 1&#xff0e;脚本格式 脚本…

体验管理|“以旧换新”重塑“家电和耐用品”低频的消费需求

Guofu 第 96⭐️ 篇原创文章分享 &#xff08;点击&#x1f446;&#x1f3fb;上方卡片关注我&#xff0c;加⭐️星标⭐️~&#xff09; &#x1f68f; 写在前面 消费者越来越注重产品或服务的体验&#xff0c;而品牌也在竭力提供更好的体验来吸引和保留消费者。 &#x1f926;…

华为OD机试真题 Java 实现【火车进站】【牛客练习题】

一、题目描述 给定一个正整数N代表火车数量&#xff0c;0<N<10&#xff0c;接下来输入火车入站的序列&#xff0c;一共N辆火车&#xff0c;每辆火车以数字1-9编号&#xff0c;火车站只有一个方向进出&#xff0c;同时停靠在火车站的列车中&#xff0c;只有后进站的出站了…

基于SpringBoot+Vue的小区物业管理系统设计与实现

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架下…

【Android】拾物App期末作业

一、期末作业题目 《校园失物/拾物APP》 二、实施目的 通过本实训&#xff0c;使受训者可以深入理解Android相关技术&#xff0c;并将所学知识应用到实际的中等规模的程序设计中。同时&#xff0c;通过本实训&#xff0c;受训者可以拓展Android相关的知识&#xff0c;提升受训者…

修改EMC VNX的管理口IP地址

本文详细介绍如何修改EMC VNX存储的管理口的IP地址&#xff0c;注意Unifed 和block的VNX存储方法是有所不同的&#xff0c;这也是有好几个朋友问我这个问题&#xff0c;写这篇博客的原因。 在修改管理口的IP地址之前&#xff0c;要确保存储不在storage domain中&#xff0c;如…

MTK 人像双摄虚化导通流程

和你一起终身学习&#xff0c;这里是程序员Android 经典好文推荐&#xff0c;通过阅读本文&#xff0c;您将收获以下知识点: 一、 创建 LogicalCameraName 相关的metadata二、 导通 LogicalCameraName Middleware 相关配置三、认 LogicalCameraName 是否创建成功四、双摄bringu…

用自然语言分割一切图像【lang-segment-anything】

近年来&#xff0c;计算机视觉取得了显着进步&#xff0c;特别是在图像分割和目标检测任务方面。 Segment Anything Model (SAM) 是最近的一项重大突破&#xff0c;这是一种多功能的深度学习模型&#xff0c;旨在有效地从图像和输入提示中预测对象掩码。 通过利用强大的编码器和…

听说最近AI应用爆了?!来AI Studio玩转大模型应用

‍ 飞桨 AI Studio 是基于百度深度学习平台飞桨&#xff08;PaddlePaddle&#xff09;的人工智能学习与实训社区&#xff0c;支持一站式模型在线开发与应用。提供优质开源模型、丰富的开源内容、功能强大的在线编程环境、云端超强 GPU 算力及存储资源&#xff0c;帮助开发者们快…

Docker镜像更新通知器DIUN

什么是 DIUN ? Docker Image Update Notifier 是一个用 Go 编写的 CLI 应用程序&#xff0c;可作为单个可执行文件和 Docker 映像交付&#xff0c;用于当 Docker 映像在 Docker registry中更新时接收通知。 和老苏之前介绍过的 watchtower 不同&#xff0c;DIUN 只是通知&…

基于Grafana的WebUI添加Prometheus数据源(三)

基于Grafana的WebUI添加Prometheus数据源(三) 1、 Grafana 介绍 1.1 什么是Grafana Grafana是一款近几年比较流行的开源数据绘图工具平台。Grafana原生支持包括但不限于InfluxDB&#xff0c;MySQL&#xff0c;OpenTSDB&#xff0c;PostgreSQL和Prometheus等多种数据源作为输入源…

勤于奋讲解国外LEAD做任务前的准备

欢迎关注勤于奋 每天12点准时更新国外LEAD相关技术 今天跟大家分享一些做国外LEAD一些资源&#xff0c;就是做国外LEAD前IP的检查&#xff0c;有人不是很清楚&#xff0c;今天分享一些检查工具。 第一个当然是whoer&#xff0c;这个工具我用的最多&#xff0c;很早的时候就使…