“分页”到“条件分页”
在后端开发领域,基础分页查询能够实现按照页码和每页条数来获取数据,为用户提供了一种基本的数据浏览方式。然而,随着业务场景变得日益复杂,用户对数据筛选的精准度提出了更高要求。例如,在员工管理系统中,用户可能希望查询姓名中包含“张”字、性别为男性且在2020年入职的员工信息。这种将分页功能与多种筛选条件相结合的查询方式,即条件分页查询,成为了企业级应用中的高频需求。它不仅考验开发者对动态SQL、参数传递以及分层架构的理解与运用,更是提升系统数据查询效率和用户体验的关键所在。
条件分页查询需求解析:用户到底需要什么?
核心功能
- 模糊查询:以员工姓名作为查询条件,实现模糊匹配。例如,当用户输入“张”时,系统能够匹配出“张三”“张四”等姓名中包含该字的员工信息。这种查询方式为用户提供了一种灵活的检索手段,能够快速定位相关人员。
- 精确匹配:依据性别进行筛选,通过设定“1”代表男性,“2”代表女性,用户可以精准获取特定性别的员工数据。精确匹配在一些对性别有特定要求的业务场景中非常实用,如统计某一性别的员工数量或查看特定性别员工的详细信息。
- 范围查询:根据员工的入职时间进行范围筛选。用户可以指定一个时间段,如“2020 - 01 - 01”至“2023 - 12 - 31”,系统将返回在这个时间段内入职的员工列表。范围查询有助于用户了解特定时期内的员工入职情况,为企业的人力资源规划提供数据支持。
附加要求
- 结果排序:查询结果按照
update_time字段进行降序排列。这意味着最新更新的员工信息将优先展示,使用户能够及时获取最新的数据变化,对于关注员工信息动态更新的场景尤为重要。 - 分页条显示:在分页条上清晰地显示当前页码和总记录数,例如“第1页,共50条”。这种直观的展示方式让用户对数据的整体分布和当前所在位置一目了然,方便用户进行分页导航操作。
接口文档详解:条件分页的“契约”
请求参数(GET方式,querystring传递)
| 参数名 | 类型 | 说明 | 是否必填 | 默认值 |
|---|---|---|---|---|
| page | Integer | 页码 | 否 | 1 |
| pageSize | Integer | 每页条数 | 否 | 10 |
| name | String | 姓名(模糊查询) | 否 | - |
| gender | Integer | 性别(1 = 男,2 = 女,精确匹配) | 否 | - |
| begin | LocalDate | 入职开始时间(范围查询) | 否 | - |
| end | LocalDate | 入职结束时间(范围查询) | 否 | - |
这些参数共同构成了用户与系统之间进行条件分页查询的交互基础。用户通过在URL中传递这些参数,向系统传达自己的查询意图。其中,page和pageSize控制分页的具体行为,name、gender、begin和end则用于设置查询条件。
响应格式(标准Result对象)
{
"code": 1,
"msg": "success",
"data": {
"total": 28, // 符合条件的总记录数
"rows": [ // 当前页数据列表(员工信息 + 部门名称)
{"id": 1, "name": "张三", "gender": 1, "entryDate": "2020 - 05 - 10", "deptName": "研发部", ...}
]
}
}
系统以这种标准的JSON格式进行响应,其中code表示响应状态码,“1”代表成功,“0”代表失败,方便前端判断请求是否成功处理。msg字段用于返回提示信息,如操作结果的描述。data对象包含了本次查询的关键数据,total表示符合查询条件的总记录数,前端可以据此计算总页数,rows则是当前页的数据列表,包含了员工的详细信息以及部门名称等相关数据。
三层架构实战:条件分页的完整实现
1. Controller层:参数接收与请求分发
核心职责
Controller层作为前后端交互的桥梁,主要负责接收来自前端的分页参数和条件参数,并对参数格式进行验证,特别是日期参数的格式。在确保参数格式正确后,将这些参数传递给Service层进行后续处理。
关键代码与解析
@RestController
@RequestMapping("/emps")
@Slf4j
public class EmpController {
@Autowired
private EmpService empService;
@GetMapping
public Result<PageResult<Emp>> getEmpByCondition(
// 分页参数(带默认值)
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer pageSize,
// 条件参数(非必填)
String name,
Integer gender,
// 日期参数:指定格式,与前端传递的"yyyy - MM - dd"匹配
@DateTimeFormat(pattern = "yyyy - MM - dd") LocalDate begin,
@DateTimeFormat(pattern = "yyyy - MM - dd") LocalDate end
) {
// 记录所有参数,便于调试
log.info("条件分页查询:page={}, pageSize={}, name={}, gender={}, begin={}, end={}",
page, pageSize, name, gender, begin, end);
// 调用Service层,传递所有参数
PageResult<Emp> pageResult = empService.pageByCondition(page, pageSize, name, gender, begin, end);
return Result.success(pageResult);
}
}
在这段代码中,@RestController注解表明该类用于处理RESTful请求,@RequestMapping("/emps")设置了类级别的请求路径前缀。@GetMapping标注的getEmpByCondition方法处理GET请求,接收前端传递的参数。@RequestParam用于获取请求参数,并为page和pageSize设置了默认值。对于日期参数begin和end,使用@DateTimeFormat(pattern = "yyyy - MM - dd")指定了日期格式,确保能够正确解析前端传递的日期字符串。log.info用于记录所有参数,方便在调试过程中查看参数值。最后,调用empService的pageByCondition方法,并将结果封装为Result对象返回给前端。日期参数格式的正确指定是这部分代码的关键,否则会导致日期解析错误,影响查询功能的正常运行。
2. Service层:分页逻辑与条件传递
核心职责
Service层在整个条件分页查询过程中起着承上启下的作用。它利用PageHelper插件开启分页功能,将从Controller层接收到的条件参数传递给Mapper层进行数据库查询,然后将查询结果进行解析和封装,返回给Controller层。
关键代码与解析
@Service
public class EmpServiceImpl implements EmpService {
@Autowired
private EmpMapper empMapper;
@Override
public PageResult<Emp> pageByCondition(Integer page, Integer pageSize,
String name, Integer gender,
LocalDate begin, LocalDate end) {
// 1. 开启分页
PageHelper.startPage(page, pageSize);
// 2. 执行条件查询(PageHelper会自动拦截SQL并添加分页)
List<Emp> empList = empMapper.listByCondition(name, gender, begin, end);
// 3. 解析结果(强转为Page对象获取总记录数)
Page<Emp> pageInfo = (Page<Emp>) empList;
// 4. 封装并返回
return new PageResult<>(pageInfo.getTotal(), pageInfo.getResult());
}
}
在上述代码中,@Service注解标识该类为服务层组件。pageByCondition方法接收来自Controller层的参数,首先通过PageHelper.startPage(page, pageSize)开启分页功能。接着,调用empMapper的listByCondition方法执行条件查询,PageHelper插件会自动拦截SQL语句并添加分页逻辑。查询结果返回后,将其强转为Page<Emp>类型,以便获取总记录数等分页信息。最后,将总记录数和当前页数据列表封装到PageResult对象中返回。确保条件参数完整准确地传递到Mapper层,并且与Mapper层动态SQL中的参数名一致,是保证查询逻辑正确执行的关键。
3. Mapper层:动态SQL编写(核心)
核心职责
Mapper层负责与数据库进行交互,通过编写MyBatis动态SQL实现根据不同条件进行灵活查询的功能。它根据传入的参数,动态拼接SQL语句,实现模糊查询、精确匹配和范围查询等操作。
关键代码(XML映射文件)与解析
<?xml version="1.0" encoding="UTF - 8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis - 3 - mapper.dtd">
<mapper namespace="com.itheima.mapper.EmpMapper">
<!-- 条件分页查询:根据name模糊查询、gender精确匹配、entryDate范围查询 -->
<select id="listByCondition" resultType="com.itheima.pojo.Emp">
select e.*, d.name deptName
from emp e
left join dept d on e.dept_id = d.id
where 1 = 1
<!-- 动态条件:姓名模糊查询(name不为null时拼接) -->
<if test="name != null and name != ''">
and e.name like concat('%', #{name}, '%')
</if>
<!-- 动态条件:性别精确匹配(gender不为null时拼接) -->
<if test="gender != null">
and e.gender = #{gender}
</if>
<!-- 动态条件:入职时间 >= begin(begin不为null时拼接) -->
<if test="begin != null">
and e.entry_date >= #{begin}
</if>
<!-- 动态条件:入职时间 <= end(end不为null时拼接) -->
<if test="end != null">
and e.entry_date <= #{end}
</if>
<!-- 排序:按更新时间降序 -->
order by e.update_time desc
</select>
</mapper>
在这个XML映射文件中,namespace指定了与Mapper接口对应的命名空间。select标签定义了查询语句,id属性与Mapper接口中的方法名一致,resultType指定了查询结果的封装类型。where 1 = 1是一个常用的技巧,它为后续动态拼接条件提供了一个基础,避免在所有条件都不满足时出现where关键字后无内容的语法错误。<if>标签用于实现条件的动态拼接,只有当条件满足时(如name不为null且不为空字符串),才会将相应的条件添加到SQL语句中。在模糊查询中,使用concat('%', #{name}, '%')来构建模糊匹配条件,这样可以有效避免SQL注入风险,而不能使用${name}。最后,通过order by e.update_time desc对查询结果按更新时间进行降序排列。注意SQL语句末尾不能加分号,以免PageHelper添加limit语句后出现语法错误。
- 姓名模糊查询:使用LIKE和%通配符,并通过CONCAT函数避免#{}出现在引号内,确保参数正确解析,如LIKE CONCAT(‘%’,#{name},‘%’)。
测试与避坑指南
测试场景
- 全参数查询:
/emps?page = 1&pageSize = 5&name = 张&gender = 1&begin = 2020 - 01 - 01&end = 2023 - 12 - 31,这种场景用于测试系统在所有条件都被设置的情况下,能否正确返回符合条件的分页数据。 - 部分参数查询:
/emps?gender = 2,此场景验证系统在只设置部分条件(这里仅设置性别为女性)时,是否能准确筛选出相关数据并进行分页展示。 - 无参数查询:
/emps,用于检查系统在未传递任何条件参数时,是否能按照默认的分页设置返回所有员工数据。
避坑指南
- 日期格式不匹配:前端传递的日期格式必须严格与
@DateTimeFormat注解中的pattern一致,例如“yyyy - MM - dd”。如果格式不一致,会导致日期解析错误,从而使查询结果不准确或查询失败。 - SQL分号问题:在Mapper层编写的SQL语句末尾一定不能加分号。因为PageHelper插件会在SQL语句后自动添加
limit语句来实现分页,如果原SQL语句末尾有分号,会导致最终生成的SQL语句语法错误,如...; limit 0,10。 - 条件逻辑错误:使用
where 1 = 1作为动态SQL的起始条件占位符是非常重要的。如果不使用它,当所有动态条件都不满足时,where关键字后将没有任何内容,从而导致SQL语法错误。 - 参数名不一致:Mapper接口的方法形参名必须与XML映射文件中
#{参数名}保持一致。否则,MyBatis无法正确将参数传递到SQL语句中,导致查询结果不准确或查询失败。


被折叠的 条评论
为什么被折叠?



