保姆级教程:在若依中无缝整合MyBatis-Plus与PageHelper(含版本冲突解决方案)

张开发
2026/5/22 2:21:03 15 分钟阅读
保姆级教程:在若依中无缝整合MyBatis-Plus与PageHelper(含版本冲突解决方案)
若依框架深度整合MyBatis-Plus与PageHelper的完美协作方案在Java企业级开发中若依(RuoYi)作为一款基于Spring Boot的快速开发框架因其丰富的功能模块和简洁的架构设计备受开发者青睐。然而当我们需要在项目中同时引入MyBatis-Plus和PageHelper这两个强大的持久层工具时版本冲突问题往往成为阻碍开发效率的绊脚石。本文将彻底解决这一痛点提供一套经过实战检验的完整整合方案。1. 环境准备与依赖管理1.1 依赖版本冲突分析MyBatis-Plus和PageHelper都依赖jsqlparser进行SQL解析但两者对jsqlparser的版本要求不同这是导致冲突的根本原因。以若依3.8.6为例内置PageHelper 1.4.6 → jsqlparser 4.5.0MyBatis-Plus 3.4.2 → jsqlparser 4.0.0解决方案核心思路通过Maven的exclusions排除冲突依赖统一使用MyBatis-Plus提供的jsqlparser版本。1.2 POM文件关键配置在父工程的pom.xml中定义版本属性properties pagehelper.boot.version1.4.6/pagehelper.boot.version mybatis-plus.version3.4.2/mybatis-plus.version hutool.version5.2.5/hutool.version /properties在需要使用的模块中添加以下依赖配置!-- PageHelper with exclusions -- dependency groupIdcom.github.pagehelper/groupId artifactIdpagehelper-spring-boot-starter/artifactId version${pagehelper.boot.version}/version exclusions exclusion artifactIdmybatis-spring/artifactId groupIdorg.mybatis/groupId /exclusion exclusion artifactIdjsqlparser/artifactId groupIdcom.github.jsqlparser/groupId /exclusion /exclusions /dependency !-- MyBatis-Plus 核心依赖 -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version${mybatis-plus.version}/version /dependency !-- Hutool工具包可选 -- dependency groupIdcn.hutool/groupId artifactIdhutool-all/artifactId version${hutool.version}/version /dependency2. 核心配置类实现2.1 MyBatis-Plus分页配置创建MybatisPlusConfig配置类Configuration public class MybatisPlusConfig { /** * 分页插件配置 */ Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); // 添加分页拦截器指定数据库类型为MySQL interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } /** * 自动填充处理器 */ Bean public MetaObjectHandler metaObjectHandler() { return new MybatisPlusMetaObjectHandler(); } }2.2 SQL注入防护配置创建SqlFilterConfig类防止SQL注入Slf4j public class SqlFilterArgumentResolver implements HandlerMethodArgumentResolver { private static final String[] SQL_KEYWORDS {master, truncate, insert, select, delete, update, declare, alter, drop, sleep}; Override public boolean supportsParameter(MethodParameter parameter) { return parameter.getParameterType().equals(Page.class); } Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) { HttpServletRequest request webRequest.getNativeRequest(HttpServletRequest.class); Page? page new Page(); // 处理分页参数 String current request.getParameter(current); String size request.getParameter(size); if (StringUtils.isNotBlank(current)) { page.setCurrent(Long.parseLong(current)); } if (StringUtils.isNotBlank(size)) { page.setSize(Long.parseLong(size)); } // 处理排序参数 processOrderItems(request, page); return page; } private void processOrderItems(HttpServletRequest request, Page? page) { String[] ascs request.getParameterValues(ascs); String[] descs request.getParameterValues(descs); ListOrderItem orderItems new ArrayList(); if (ascs ! null) { Arrays.stream(ascs) .filter(this::isSafeSql) .map(OrderItem::asc) .forEach(orderItems::add); } if (descs ! null) { Arrays.stream(descs) .filter(this::isSafeSql) .map(OrderItem::desc) .forEach(orderItems::add); } page.addOrder(orderItems); } private boolean isSafeSql(String sql) { for (String keyword : SQL_KEYWORDS) { if (StringUtils.containsIgnoreCase(sql, keyword)) { log.warn(检测到潜在SQL注入风险: {}, sql); return false; } } return true; } }3. 业务层改造与适配3.1 Mapper层调整所有Mapper接口需要改为继承MyBatis-Plus的BaseMapperpublic interface UserMapper extends BaseMapperUser { // 原有自定义方法可以保留 ListUser selectCustomList(UserQuery query); }3.2 Service层改造Service接口和实现类也需要相应调整// 接口定义 public interface IUserService extends IServiceUser { TableDataInfo selectUserList(UserQuery query); } // 实现类 Service public class UserServiceImpl extends ServiceImplUserMapper, User implements IUserService { Override public TableDataInfo selectUserList(UserQuery query) { // 启动PageHelper分页 PageHelper.startPage(query.getPageNum(), query.getPageSize()); ListUser list baseMapper.selectCustomList(query); PageInfoUser pageInfo new PageInfo(list); // 返回若依标准分页格式 return getDataTable(list, pageInfo.getTotal()); } }3.3 Controller层适配在BaseController中添加分页数据包装方法protected T TableDataInfo getDataTable(ListT list) { TableDataInfo rspData new TableDataInfo(); rspData.setCode(HttpStatus.SUCCESS); rspData.setMsg(查询成功); rspData.setRows(list); // 从PageInfo中获取总记录数 if (list instanceof Page) { rspData.setTotal(((Page?) list).getTotal()); } else { rspData.setTotal(list.size()); } return rspData; }4. 实战应用与测试验证4.1 传统PageHelper分页示例GetMapping(/list) public TableDataInfo list(User user) { // PageHelper方式分页 PageHelper.startPage(user.getPageNum(), user.getPageSize()); ListUser list userService.selectUserList(user); return getDataTable(list); }4.2 MyBatis-Plus分页示例GetMapping(/page) public TableDataInfo page(PageUser page, User user) { // MyBatis-Plus方式分页 LambdaQueryWrapperUser wrapper new LambdaQueryWrapper(); wrapper.eq(StringUtils.isNotBlank(user.getName()), User::getName, user.getName()); PageUser result userService.page(page, wrapper); return getDataTable(result); }4.3 复杂查询分页处理对于需要复杂SQL查询的场景public TableDataInfo selectComplexList(ComplexQuery query) { // 第一种方式使用PageHelper PageHelper.startPage(query.getPageNum(), query.getPageSize()); ListComplexData list mapper.selectComplexData(query); return getDataTable(list); // 第二种方式使用MyBatis-Plus PageComplexData page new Page(query.getPageNum(), query.getPageSize()); IPageComplexData result mapper.selectComplexDataPage(page, query); return getDataTable(result); }5. 常见问题解决方案5.1 分页插件不生效检查清单配置检查确保Configuration类被正确扫描检查yml中mybatis-plus配置是否正确mybatis-plus: mapper-locations: classpath*:mapper/**/*Mapper.xml type-aliases-package: com.ruoyi.**.domain依赖冲突排查执行mvn dependency:tree查看依赖树确保jsqlparser只有一个版本代码层面验证在Interceptor中添加日志输出检查SQL日志是否包含LIMIT语句5.2 性能优化建议COUNT查询优化// 在Page构造时设置不进行COUNT查询 PageUser page new Page(pageNum, pageSize, false);多表联查分页对于复杂联表查询建议手动编写COUNT语句使用MyBatis-Plus的SqlParser注解过滤不需要的表PageHelper与MyBatis-Plus选择策略场景推荐方案理由简单单表查询MyBatis-Plus分页零配置自动优化复杂SQL或存储过程PageHelper对复杂SQL支持更好需要自定义COUNT逻辑混合使用结合两者优势6. 高级应用技巧6.1 动态表名处理在MyBatis-Plus中实现动态表名public class DynamicTableNameParser implements IKeyGenerator { Override public String process(MappedStatement ms, Object parameter, String tableName) { // 根据业务逻辑动态返回表名 if (parameter instanceof BaseEntity) { return ((BaseEntity) parameter).getDynamicTableName(); } return tableName; } } // 配置类中添加 Bean public DynamicTableNameParser dynamicTableNameParser() { return new DynamicTableNameParser(); }6.2 多数据源分页支持对于多数据源项目需要为每个数据源单独配置分页插件Bean ConfigurationProperties(prefix spring.datasource.druid.master) public DataSource masterDataSource() { return DruidDataSourceBuilder.create().build(); } Bean public MybatisPlusInterceptor masterInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } // 从数据源配置类似6.3 自定义分页参数接收前端传递分页参数多样化处理GetMapping(/customPage) public TableDataInfo customPage( RequestParam(defaultValue 1) Integer pageNo, RequestParam(defaultValue 10) Integer pageSize, RequestParam(required false) String sortField, RequestParam(required false) String sortOrder) { PageUser page new Page(pageNo, pageSize); if (StringUtils.isNotBlank(sortField)) { if (asc.equalsIgnoreCase(sortOrder)) { page.addOrder(OrderItem.asc(sortField)); } else { page.addOrder(OrderItem.desc(sortField)); } } return getDataTable(userService.page(page)); }在实际项目开发中我们发现MyBatis-Plus的分页性能在简单场景下表现更优而PageHelper对于复杂SQL的支持更为灵活。通过本文的整合方案团队可以根据具体业务需求灵活选择分页方式同时避免了版本冲突带来的各种隐性问题。

更多文章