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

## 一、功能特点

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

## 二、基本使用

### 1. 简单查询

```java
// 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. 分页查询

```java
@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. 模糊查询

#### 方式一：指定字段模糊查询（推荐）

```java
@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：**
```sql
WHERE name LIKE '%张%' AND phone LIKE '%138%'
```

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

```java
@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后缀字段（仅用于查询，不需要对应数据库字段）：

```java
// 查询实体类（继承基础实体类）
public class LawyerUserQuery extends LawyerUser {
    private String name_Like;  // 模糊查询姓名
    private String phone_Like;  // 模糊查询手机号
}
```

```java
// 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后缀字段：

```java
// 查询实体类（继承基础实体类）
public class LawyerUserQuery extends LawyerUser {
    private List<Integer> id_List;  // 批量查询ID
    private List<String> status_List;  // 批量查询状态
}
```

```java
// 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：**
```sql
WHERE id IN (1, 2, 3)
```

### 5. 范围查询

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

```java
// 查询实体类（继承基础实体类）
public class LawyerUserQuery extends LawyerUser {
    private Date createdTime_Start;  // 创建时间开始
    private Date createdTime_End;    // 创建时间结束
    private Integer serviceCount_Start;  // 服务次数范围开始
    private Integer serviceCount_End;    // 服务次数范围结束
}
```

```java
// 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：**
```sql
WHERE created_time >= '2025-01-01' AND created_time <= '2025-01-31'
```

## 三、组合查询示例

### 复杂查询场景

```java
@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：**
```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)` - 创建查询构建器
```java
QueryBuilder.of(queryEntity)
```

#### 2. `ignoreEmptyStr(boolean ignore)` - 设置是否忽略空字符串
```java
QueryBuilder.of(query)
    .ignoreEmptyStr(true)   // 默认true，忽略空字符串
    .build()
    .list(service);

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

#### 3. `stringFieldLike(boolean like)` - 设置String字段是否默认模糊查询
```java
QueryBuilder.of(query)
    .stringFieldLike(true)   // 所有String字段使用模糊查询
    .build()
    .list(service);

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

#### 4. `likeFields(String... fieldNames)` - 指定字段使用模糊查询
```java
QueryBuilder.of(query)
    .likeFields("name")              // 单个字段
    .build()
    .list(service);

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

#### 5. `page(int pageNum, int pageSize)` - 设置分页参数
```java
QueryBuilder.of(query)
    .page(1, 10)  // 第1页，每页10条
    .build()
    .page(service);
```

#### 6. `page(Page<T> page)` - 设置分页对象
```java
Page<LawyerUser> pageObj = new Page<>(1, 10);
QueryBuilder.of(query)
    .page(pageObj)
    .build()
    .page(service);
```

#### 7. `build()` - 构建查询条件
```java
QueryResult<LawyerUser> result = QueryBuilder.of(query)
    .build();  // 返回 QueryResult 对象
```

### QueryResult 方法

#### 1. `list(IService<T> service)` - 列表查询
```java
List<LawyerUser> list = builder.build().list(lawyerUserService);
```

#### 2. `page(IService<T> service)` - 分页查询
```java
IPage<LawyerUser> page = builder.page(1, 10).build().page(lawyerUserService);
```

#### 3. `one(IService<T> service)` - 单条查询
```java
LawyerUser user = builder.build().one(lawyerUserService);
```

#### 4. `count(IService<T> service)` - 计数查询
```java
long count = builder.build().count(lawyerUserService);
```

#### 5. `getWrapper()` - 获取查询条件（用于调试）
```java
QueryWrapper<LawyerUser> wrapper = builder.build().getWrapper();
System.out.println(wrapper.getTargetSql());  // 打印SQL
```

## 五、命名规则

### 1. 模糊查询字段

**方式一：使用 likeFields 方法指定**
```java
.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_Start` 和 `xxx_End`
- 示例：
  - `createdTime_Start` → `createdTime >= value`
  - `createdTime_End` → `createdTime <= value`

## 六、查询优先级

### 模糊查询优先级

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

### 示例

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

**结果：**
- `name` 字段：使用模糊查询（`likeFields` 优先级更高）
- 其他String字段：使用模糊查询（`stringFieldLike` 生效）
- 非String字段：使用等值查询

## 七、完整示例

### 实体类定义

```java
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 完整示例

```java
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` 属性
- **默认规则**：驼峰命名转下划线命名（`userName` → `user_name`）

### 3. 序列化安全

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

### 4. 性能考虑

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

### 5. 日期处理

- 日期字段会自动处理空字符串绑定问题
- 支持 `java.util.Date`、`java.sql.Date`、`LocalDate`、`LocalDateTime`

## 九、常见问题

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

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

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

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

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

### Q3: 如何查询空值？

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

### Q4: 如何调试生成的SQL？

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

## 十、最佳实践

### 1. 推荐使用方式

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

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

### 2. 不推荐使用方式

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

### 3. 性能优化建议

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

---

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