QueryBuilder使用示例.md 15 KB

QueryBuilder 通用查询工具类使用指南

一、功能特点

  • 自动构建查询条件:根据对象非空字段自动生成查询条件
  • 支持分页查询:内置分页功能
  • 支持模糊查询:可指定字段或全局设置String字段模糊查询
  • 支持批量查询:集合类型字段自动使用IN查询
  • 支持范围查询:识别Start/End后缀字段
  • 链式调用:流畅的API设计
  • 类型安全:自动处理序列化问题,避免类型转换错误
  • 字段过滤:自动跳过静态字段、transient字段、不存在字段

二、基本使用

1. 简单查询

// Controller中
@GetMapping("/getList")
public R<List<LawyerUser>> getList(@ModelAttribute LawyerUser query) {
    List<LawyerUser> list = QueryBuilder.of(query)
        .build()
        .list(lawyerUserService);
    return R.data(list);
}

请求示例:

GET /lawyer/user/getList?name=张三&status=1

说明:

  • 所有非空字段都会作为等值查询条件(=
  • String类型字段默认使用等值查询

2. 分页查询

@GetMapping("/getPage")
public R<IPage<LawyerUser>> getPage(
    @ModelAttribute LawyerUser query,
    @RequestParam(defaultValue = "1") int pageNum,
    @RequestParam(defaultValue = "10") int pageSize) {
    
    IPage<LawyerUser> page = QueryBuilder.of(query)
        .page(pageNum, pageSize)  // 设置分页
        .build()
        .page(lawyerUserService);  // 执行分页查询
    
    return R.data(page);
}

3. 模糊查询

方式一:指定字段模糊查询(推荐)

@GetMapping("/search")
public R<List<LawyerUser>> search(@ModelAttribute LawyerUser query) {
    List<LawyerUser> list = QueryBuilder.of(query)
        .likeFields("name", "phone")  // 指定 name 和 phone 字段使用模糊查询
        .build()
        .list(lawyerUserService);
    return R.data(list);
}

请求示例:

GET /lawyer/user/search?name=张&phone=138

生成的SQL:

WHERE name LIKE '%张%' AND phone LIKE '%138%'

方式二:全局String字段模糊查询

@GetMapping("/search")
public R<List<LawyerUser>> search(@ModelAttribute LawyerUser query) {
    List<LawyerUser> list = QueryBuilder.of(query)
        .stringFieldLike(true)  // 所有String类型字段都使用模糊查询
        .build()
        .list(lawyerUserService);
    return R.data(list);
}

方式三:使用_Like后缀字段(自动识别)

在实体类中添加_Like后缀字段(仅用于查询,不需要对应数据库字段):

// 查询实体类(继承基础实体类)
public class LawyerUserQuery extends LawyerUser {
    private String name_Like;  // 模糊查询姓名
    private String phone_Like;  // 模糊查询手机号
}
// Controller中
@GetMapping("/search")
public R<List<LawyerUser>> search(@ModelAttribute LawyerUserQuery query) {
    List<LawyerUser> list = QueryBuilder.of(query)
        .build()
        .list(lawyerUserService);
    return R.data(list);
}

请求示例:

GET /lawyer/user/search?name_Like=张&phone_Like=138

4. 批量查询(IN查询)

在实体类中添加_List后缀字段:

// 查询实体类(继承基础实体类)
public class LawyerUserQuery extends LawyerUser {
    private List<Integer> id_List;  // 批量查询ID
    private List<String> status_List;  // 批量查询状态
}
// Controller中
@GetMapping("/getByIds")
public R<List<LawyerUser>> getByIds(@ModelAttribute LawyerUserQuery query) {
    List<LawyerUser> list = QueryBuilder.of(query)
        .build()
        .list(lawyerUserService);
    return R.data(list);
}

请求示例:

GET /lawyer/user/getByIds?id_List=1&id_List=2&id_List=3

生成的SQL:

WHERE id IN (1, 2, 3)

5. 范围查询

在实体类中添加_Start/_End后缀字段:

// 查询实体类(继承基础实体类)
public class LawyerUserQuery extends LawyerUser {
    private Date createdTime_Start;  // 创建时间开始
    private Date createdTime_End;    // 创建时间结束
    private Integer serviceCount_Start;  // 服务次数范围开始
    private Integer serviceCount_End;    // 服务次数范围结束
}
// Controller中
@GetMapping("/getByTimeRange")
public R<List<LawyerUser>> getByTimeRange(@ModelAttribute LawyerUserQuery query) {
    List<LawyerUser> list = QueryBuilder.of(query)
        .build()
        .list(lawyerUserService);
    return R.data(list);
}

请求示例:

GET /lawyer/user/getByTimeRange?createdTime_Start=2025-01-01&createdTime_End=2025-01-31

生成的SQL:

WHERE created_time >= '2025-01-01' AND created_time <= '2025-01-31'

三、组合查询示例

复杂查询场景

@GetMapping("/complexQuery")
public R<IPage<LawyerUser>> complexQuery(
    @ModelAttribute LawyerUserQuery query,
    @RequestParam(defaultValue = "1") int pageNum,
    @RequestParam(defaultValue = "10") int pageSize) {
    
    IPage<LawyerUser> page = QueryBuilder.of(query)
        .page(pageNum, pageSize)           // 分页
        .likeFields("name", "phone")        // 指定字段模糊查询
        .ignoreEmptyStr(true)               // 忽略空字符串(默认true)
        .build()
        .page(lawyerUserService);
    
    return R.data(page);
}

请求示例:

GET /lawyer/user/complexQuery?name=张&phone=138&status=1&createdTime_Start=2025-01-01&createdTime_End=2025-01-31&pageNum=1&pageSize=10

生成的SQL:

WHERE name LIKE '%张%' 
  AND phone LIKE '%138%'
  AND status = 1
  AND created_time >= '2025-01-01'
  AND created_time <= '2025-01-31'
LIMIT 10 OFFSET 0

四、API方法说明

QueryBuilder 方法

1. of(T queryEntity) - 创建查询构建器

QueryBuilder.of(queryEntity)

2. ignoreEmptyStr(boolean ignore) - 设置是否忽略空字符串

QueryBuilder.of(query)
    .ignoreEmptyStr(true)   // 默认true,忽略空字符串
    .build()
    .list(service);

QueryBuilder.of(query)
    .ignoreEmptyStr(false)  // 保留空字符串作为查询条件
    .build()
    .list(service);

3. stringFieldLike(boolean like) - 设置String字段是否默认模糊查询

QueryBuilder.of(query)
    .stringFieldLike(true)   // 所有String字段使用模糊查询
    .build()
    .list(service);

QueryBuilder.of(query)
    .stringFieldLike(false)  // 所有String字段使用等值查询(默认)
    .build()
    .list(service);

4. likeFields(String... fieldNames) - 指定字段使用模糊查询

QueryBuilder.of(query)
    .likeFields("name")              // 单个字段
    .build()
    .list(service);

QueryBuilder.of(query)
    .likeFields("name", "phone")     // 多个字段
    .build()
    .list(service);

5. page(int pageNum, int pageSize) - 设置分页参数

QueryBuilder.of(query)
    .page(1, 10)  // 第1页,每页10条
    .build()
    .page(service);

6. page(Page<T> page) - 设置分页对象

Page<LawyerUser> pageObj = new Page<>(1, 10);
QueryBuilder.of(query)
    .page(pageObj)
    .build()
    .page(service);

7. build() - 构建查询条件

QueryResult<LawyerUser> result = QueryBuilder.of(query)
    .build();  // 返回 QueryResult 对象

QueryResult 方法

1. list(IService<T> service) - 列表查询

List<LawyerUser> list = builder.build().list(lawyerUserService);

2. page(IService<T> service) - 分页查询

IPage<LawyerUser> page = builder.page(1, 10).build().page(lawyerUserService);

3. one(IService<T> service) - 单条查询

LawyerUser user = builder.build().one(lawyerUserService);

4. count(IService<T> service) - 计数查询

long count = builder.build().count(lawyerUserService);

5. getWrapper() - 获取查询条件(用于调试)

QueryWrapper<LawyerUser> wrapper = builder.build().getWrapper();
System.out.println(wrapper.getTargetSql());  // 打印SQL

五、命名规则

1. 模糊查询字段

方式一:使用 likeFields 方法指定

.likeFields("name", "phone")  // 在代码中指定

方式二:字段名以 _Like 结尾

  • 字段名以 _Like 结尾
  • 示例:name_Like → 查询 name 字段,使用 LIKE '%value%'

2. 批量查询字段

  • 字段类型为 Collection(List、Set等)
  • 字段名以 _List 结尾(推荐)
  • 示例:id_List → 查询 id 字段,使用 IN (1,2,3)

3. 范围查询字段

  • 字段名以 _Start_End 结尾
  • 成对出现:xxx_Startxxx_End
  • 示例:
    • createdTime_StartcreatedTime >= value
    • createdTime_EndcreatedTime <= value

六、查询优先级

模糊查询优先级

  1. 最高优先级likeFields() 方法指定的字段
  2. 次优先级:字段名以 _Like 结尾的字段
  3. 全局设置stringFieldLike(true) 时,所有String字段使用模糊查询
  4. 默认:等值查询(=

示例

QueryBuilder.of(query)
    .stringFieldLike(true)      // 全局:所有String字段模糊查询
    .likeFields("name")         // 指定:name字段模糊查询(优先级更高)
    .build()
    .list(service);

结果:

  • name 字段:使用模糊查询(likeFields 优先级更高)
  • 其他String字段:使用模糊查询(stringFieldLike 生效)
  • 非String字段:使用等值查询

七、完整示例

实体类定义

import java.util.Date;
import java.util.List;
import lombok.Data;

@Data
public class LawyerUserQuery extends LawyerUser {
    // 模糊查询字段(方式一:在代码中指定,不需要在实体类中定义)
    // 方式二:使用_Like后缀字段
    private String name_Like;
    private String phone_Like;
    
    // 批量查询字段
    private List<Integer> id_List;
    private List<Integer> status_List;
    
    // 范围查询字段
    private Date createdTime_Start;
    private Date createdTime_End;
    private Integer serviceCount_Start;
    private Integer serviceCount_End;
}

Controller 完整示例

import org.springframework.web.bind.annotation.*;
import lombok.RequiredArgsConstructor;
import com.baomidou.mybatisplus.core.metadata.IPage;

@RestController
@RequestMapping("/lawyer/user")
@RequiredArgsConstructor
public class LawyerUserController {
    
    private final LawyerUserService lawyerUserService;
    
    /**
     * 通用查询接口(支持所有查询方式)
     */
    @GetMapping("/query")
    public R<IPage<LawyerUser>> query(
        @ModelAttribute LawyerUserQuery query,
        @RequestParam(defaultValue = "1") int pageNum,
        @RequestParam(defaultValue = "10") int pageSize) {
        
        IPage<LawyerUser> page = QueryBuilder.of(query)
            .page(pageNum, pageSize)
            .likeFields("name", "phone")  // 指定字段模糊查询
            .build()
            .page(lawyerUserService);
        
        return R.data(page);
    }
    
    /**
     * 简单查询(只使用等值查询)
     */
    @GetMapping("/simple")
    public R<List<LawyerUser>> simple(@ModelAttribute LawyerUser query) {
        List<LawyerUser> list = QueryBuilder.of(query)
            .build()
            .list(lawyerUserService);
        return R.data(list);
    }
    
    /**
     * 全局模糊查询(所有String字段都模糊查询)
     */
    @GetMapping("/search")
    public R<List<LawyerUser>> search(@ModelAttribute LawyerUser query) {
        List<LawyerUser> list = QueryBuilder.of(query)
            .stringFieldLike(true)  // 所有String字段模糊查询
            .build()
            .list(lawyerUserService);
        return R.data(list);
    }
}

八、注意事项

1. 字段过滤规则

  • 自动跳过:静态字段(static)、transient字段(transient
  • 自动跳过@TableField(exist = false) 标记的字段
  • 自动跳过:空值(null)、空字符串(如果启用 ignoreEmptyStr
  • 自动跳过:空集合

2. 字段名映射

  • 优先使用@TableField 注解的 value 属性
  • 默认规则:驼峰命名转下划线命名(userNameuser_name

3. 序列化安全

  • 自动过滤不可序列化的对象
  • 只支持基本类型:Number、String、Boolean、Date等
  • 集合元素会自动过滤不可序列化的项

4. 性能考虑

  • 批量查询(IN)建议限制集合大小,避免SQL过长
  • 模糊查询(LIKE)在大数据量时可能较慢,建议添加索引

5. 日期处理

  • 日期字段会自动处理空字符串绑定问题
  • 支持 java.util.Datejava.sql.DateLocalDateLocalDateTime

九、常见问题

Q1: 如何让某个String字段使用等值查询,其他字段使用模糊查询?

// 方式一:使用全局模糊查询,然后排除特定字段
QueryBuilder.of(query)
    .stringFieldLike(true)  // 全局模糊查询
    .build()
    .list(service);
// 注意:没有单独排除的方法,可以使用Like后缀字段的方式
    
// 方式二:只指定需要模糊查询的字段
QueryBuilder.of(query)
    .likeFields("name", "phone")  // 只指定这些字段模糊查询
    .build()
    .list(service);

Q2: 如何同时使用等值查询和模糊查询?

// 在实体类中定义两个字段
// 查询实体类(继承基础实体类)
public class LawyerUserQuery extends LawyerUser {
    private String name;        // 等值查询
    private String name_Like;    // 模糊查询
}

// 使用时
QueryBuilder.of(query)
    .build()  // name使用等值查询,name_Like使用模糊查询
    .list(service);

Q3: 如何查询空值?

QueryBuilder.of(query)
    .ignoreEmptyStr(false)  // 不忽略空字符串
    .build()
    .list(service);

Q4: 如何调试生成的SQL?

QueryResult<LawyerUser> result = QueryBuilder.of(query).build();
QueryWrapper<LawyerUser> wrapper = result.getWrapper();
System.out.println(wrapper.getTargetSql());  // 打印SQL语句

十、最佳实践

1. 推荐使用方式

// ✅ 推荐:指定字段模糊查询
QueryBuilder.of(query)
    .likeFields("name", "phone")
    .build()
    .list(service);

// ✅ 推荐:使用_Like后缀字段(更灵活)
// 在实体类中定义 name_Like 字段
QueryBuilder.of(query)
    .build()
    .list(service);

2. 不推荐使用方式

// ❌ 不推荐:全局String字段模糊查询(可能影响不需要模糊的字段)
QueryBuilder.of(query)
    .stringFieldLike(true)  // 除非你真的需要所有String字段都模糊
    .build()
    .list(service);

3. 性能优化建议

  • 模糊查询字段建议添加数据库索引
  • 批量查询(IN)建议限制集合大小(如:最多1000个)
  • 复杂查询建议使用分页,避免一次性查询大量数据

总结QueryBuilder 提供了强大而灵活的查询能力,只需要传入对象,就能自动构建各种查询条件,大大简化了开发工作。通过合理使用 likeFields()stringFieldLike() 方法,可以灵活控制模糊查询行为。