2025 若依框架实战:MyBatis分页失效排查与SQL优化指南
1. 多部门查询引发的分页失效现场还原最近在重构一个老项目时遇到了一个典型的分页失效问题。场景是这样的系统需要根据不同部门的权限返回数据列表管理员可以看到所有数据普通用户只能查看自己所属部门的数据。听起来很简单的需求对吧但实际运行时却发现分页完全失效前端明明设置了每页10条结果返回了全部几百条数据。先来看原始代码的关键片段已简化public ListSysTest selectList(String name) { SysUser user SecurityUtils.getLoginUser().getUser(); ListSysTest resultList new ArrayList(); if (user.isAdmin()) { // 管理员直接查询全部 resultList sysTestMapper.selectAll(name); } else { // 普通用户处理多部门情况 if (user.hasMultipleDepts()) { String[] deptIds user.getDeptIds().split(,); for (String deptId : deptIds) { // 循环查询每个部门的数据 ListSysTest tempList sysTestMapper.selectByDept(name, deptId); resultList.addAll(tempList); } } else { // 单个部门直接查询 ListSysTest tempList sysTestMapper.selectByDept(name, user.getDeptId()); resultList.addAll(tempList); } } return resultList; }这段代码的业务逻辑看似合理先判断用户权限然后根据部门数量决定查询方式。但为什么分页会失效呢关键在于PageHelper.startPage()的拦截机制。在实际调用链中Controller层确实调用了startPage()但最终分页效果却消失了。2. PageHelper分页原理深度剖析要理解这个问题我们需要先搞清楚MyBatis分页插件的工作原理。以最常用的PageHelper为例它的核心机制是这样的拦截点通过MyBatis的Interceptor接口在Executor.query()方法执行前进行拦截生效条件只有在startPage()之后立即执行的第一个查询语句会被分页处理分页逻辑自动计算总记录数执行COUNT查询修改原始SQL添加LIMIT子句对查询结果进行包装返回Page对象在我们的案例中问题出在Service层的处理方式上。虽然Controller调用了startPage()但Service层却通过以下方式绕过了分页循环执行多次selectByDept查询在内存中合并结果集每次循环中的Mapper查询都被视为独立的查询只有第一个会被分页最终合并的结果集自然就失去了分页效果3. SQL优化方案实战解决这个问题的核心思路是将多次查询合并为单次查询。具体来说我们需要改造SQL使其支持多部门查询。下面是详细的改造步骤3.1 Mapper接口改造首先修改Mapper接口将部门参数改为List类型ListSysTest selectByDepts( Param(name) String name, Param(deptIds) ListString deptIds );3.2 XML映射文件优化使用MyBatis的动态SQL实现多部门查询select idselectByDepts resultTypeSysTest SELECT t.* FROM sys_test t JOIN sys_dept_relation r ON t.id r.test_id WHERE r.status 1 if testname ! null and name ! AND t.name LIKE CONCAT(%, #{name}, %) /if if testdeptIds ! null and deptIds.size() 0 AND r.dept_id IN foreach collectiondeptIds itemdeptId open( separator, close) #{deptId} /foreach /if /select3.3 Service层重构简化后的Service实现public ListSysTest selectList(String name) { SysUser user SecurityUtils.getLoginUser().getUser(); if (user.isAdmin()) { return sysTestMapper.selectAll(name); } else { ListString deptIds Arrays.asList(user.getDeptIds().split(,)); return sysTestMapper.selectByDepts(name, deptIds); } }3.4 Controller层调整保持Controller不变即可GetMapping(/list) public TableDataInfo getList(String name) { startPage(); // 关键分页入口 ListSysTest list testService.selectList(name); return getDataTable(list); }4. 性能对比与优化建议改造前后的性能对比非常明显指标改造前方案优化后方案查询次数N次部门数量1次分页效果失效正常数据库压力高低网络传输量可能传输过量数据严格按分页传输代码可维护性复杂简洁在实际项目中我还总结了几个优化小技巧IN子查询优化当部门数量过多时比如超过100个可以考虑改用临时表关联查询分页参数传递确保startPage()在调用链的最外层通常是ControllerPageHelper配置建议在配置中添加reasonabletrue和pageSizeZerotrue参数索引检查确保查询条件涉及的字段如dept_id有适当索引5. 常见踩坑点排查指南在实际开发中分页失效问题可能还有以下变种异步调用导致失效在异步方法中调用分页查询需要特别注意线程上下文传递嵌套查询问题在复杂的嵌套查询中PageHelper可能无法正确拦截目标SQL手动COUNT问题当使用PageHelper.startPage(pageNum, pageSize, true)时注意自定义COUNT语句的编写规范特殊数据库方言对于Oracle等特殊数据库可能需要额外配置方言这里分享一个排查分页问题的通用流程确认startPage()的调用位置是否正确检查是否有多余的查询操作在分页查询之前执行使用MyBatis的SQL日志功能观察实际执行的SQL语句在PageHelper配置中开启helperDialect属性确保方言正确对于复杂查询考虑使用PageHelperSkip注解跳过不需要分页的方法6. 若依框架中的分页最佳实践在若依框架中分页功能已经做了很好的封装。这里分享几个框架特有的使用技巧TableDataInfo封装直接使用框架提供的getDataTable()方法返回分页数据全局分页配置在application.yml中可以配置默认分页参数前端分页联动若依前端表格组件会自动处理分页参数传递特殊分页需求对于需要自定义分页逻辑的场景可以使用PageHelper.offsetPage()一个完整的若依分页请求流程是这样的前端发起请求携带pageNum和pageSize参数Controller调用startPage()启动分页Service执行单次数据库查询框架自动拦截SQL并添加分页逻辑返回包装后的Page对象前端渲染分页组件和数据表格7. 动态SQL编写技巧进阶为了处理更复杂的业务场景这里再分享几个动态SQL的高级用法多条件组合查询示例select idselectComplex resultTypeSysTest SELECT * FROM sys_test where if testname ! null AND name LIKE #{name} /if choose when testtype 1 AND status 1 /when when testtype 2 AND status IN (2,3) /when otherwise AND status ! 0 /otherwise /choose if testdeptIds ! null AND dept_id IN foreach collectiondeptIds itemitem open( separator, close) #{item} /foreach /if /where ORDER BY create_time DESC /select批量更新示例update idbatchUpdate UPDATE sys_test trim prefixSET suffixOverrides, trim prefixname CASE suffixEND, foreach collectionlist itemitem WHEN id #{item.id} THEN #{item.name} /foreach /trim trim prefixstatus CASE suffixEND, foreach collectionlist itemitem WHEN id #{item.id} THEN #{item.status} /foreach /trim /trim WHERE id IN foreach collectionlist itemitem open( separator, close) #{item.id} /foreach /update这些技巧在复杂业务系统中非常实用既能保证代码整洁又能提高执行效率。特别是在处理分页查询时合理的SQL设计可以避免很多性能问题。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2429187.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!