logo

SpringBoot集成MyBatis-Plus:分页、条件查询与SQL日志全攻略

作者:demo2025.09.18 16:02浏览量:0

简介:本文详细介绍SpringBoot项目中集成MyBatis-Plus框架的分页插件配置、条件构造器使用及SQL打印日志的开启方法,助力开发者高效完成数据层开发。

SpringBoot集成MyBatis-Plus:分页、条件查询与SQL日志全攻略

一、MyBatis-Plus核心功能概述

MyBatis-Plus是MyBatis的增强工具,在保持原有功能基础上提供分页查询、条件构造器、Lambda表达式等特性。其核心优势在于:

  1. 零SQL编写:通过条件构造器实现复杂查询
  2. 高性能分页:内置物理分页插件
  3. 开发效率提升:减少90%的CRUD代码量
  4. SQL透明化:支持完整的SQL日志输出

在SpringBoot项目中,通过Maven引入核心依赖:

  1. <dependency>
  2. <groupId>com.baomidou</groupId>
  3. <artifactId>mybatis-plus-boot-starter</artifactId>
  4. <version>3.5.3.1</version>
  5. </dependency>

二、分页插件配置与使用

1. 插件配置原理

MyBatis-Plus分页插件通过拦截器实现物理分页,核心类为PaginationInnerInterceptor。其工作机制包含:

  • SQL重写:在原始SQL后追加LIMIT子句
  • 分页参数计算:自动处理页码与每页条数的换算
  • 结果集封装:将查询结果转换为Page对象

2. 完整配置示例

  1. @Configuration
  2. public class MybatisPlusConfig {
  3. @Bean
  4. public MybatisPlusInterceptor mybatisPlusInterceptor() {
  5. MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
  6. // 分页插件配置
  7. PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor(
  8. DbType.MYSQL // 数据库类型
  9. );
  10. paginationInterceptor.setMaxLimit(1000L); // 单页最大限制
  11. paginationInterceptor.setOverflow(true); // 溢出总页数处理
  12. interceptor.addInnerInterceptor(paginationInterceptor);
  13. return interceptor;
  14. }
  15. }

3. 分页查询实现

服务层实现示例:

  1. @Service
  2. public class UserServiceImpl implements UserService {
  3. @Autowired
  4. private UserMapper userMapper;
  5. public IPage<User> queryPage(Integer current, Integer size) {
  6. // 创建分页对象
  7. Page<User> page = new Page<>(current, size);
  8. // 执行分页查询
  9. return userMapper.selectPage(page, null);
  10. }
  11. }

控制器层调用:

  1. @GetMapping("/users")
  2. public Result getUsers(
  3. @RequestParam(defaultValue = "1") Integer pageNum,
  4. @RequestParam(defaultValue = "10") Integer pageSize) {
  5. IPage<User> page = userService.queryPage(pageNum, pageSize);
  6. return Result.success(page);
  7. }

4. 高级配置选项

配置项 类型 默认值 说明
maxLimit Long null 单页最大记录数限制
overflow Boolean false 超出最大页数处理方式
countSql Boolean true 是否执行COUNT查询
optimal Boolean false 优化COUNT查询(子查询优化)

三、条件查询构造器详解

1. Wrapper体系结构

MyBatis-Plus提供四种条件构造器:

  • QueryWrapper:基础条件构造
  • LambdaQueryWrapper:Lambda表达式支持
  • UpdateWrapper:更新条件构造
  • LambdaUpdateWrapper:Lambda更新构造

2. 常用查询方法

  1. // 1. 等于查询
  2. LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
  3. wrapper.eq(User::getAge, 25);
  4. // 2. 范围查询
  5. wrapper.between(User::getAge, 20, 30)
  6. .ge(User::getCreateTime, LocalDateTime.now().minusDays(7));
  7. // 3. 模糊查询
  8. wrapper.like(User::getName, "张")
  9. .or()
  10. .likeRight(User::getPhone, "138");
  11. // 4. 排序
  12. wrapper.orderByDesc(User::getCreateTime)
  13. .orderByAsc(User::getAge);
  14. // 5. 嵌套条件
  15. wrapper.and(w -> w.eq(User::getStatus, 1)
  16. .or()
  17. .eq(User::getType, 2));

3. 链式调用最佳实践

推荐采用分段构建方式提高可读性:

  1. public List<User> queryComplex(String keyword, Integer minAge) {
  2. LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();
  3. // 基础条件
  4. wrapper.eq(User::getStatus, 1);
  5. // 关键词搜索
  6. if (StringUtils.isNotBlank(keyword)) {
  7. wrapper.like(User::getName, keyword)
  8. .or()
  9. .like(User::getEmail, keyword);
  10. }
  11. // 年龄范围
  12. if (minAge != null) {
  13. wrapper.ge(User::getAge, minAge);
  14. }
  15. // 排序
  16. wrapper.orderByDesc(User::getCreateTime);
  17. return userMapper.selectList(wrapper);
  18. }

四、SQL打印日志配置

1. 日志输出级别配置

application.yml中配置:

  1. logging:
  2. level:
  3. com.baomidou.mybatisplus: debug
  4. com.baomidou.mybatisplus.core.conditions: trace

2. 性能分析插件

启用SQL性能分析插件:

  1. @Bean
  2. public MybatisPlusInterceptor mybatisPlusInterceptor() {
  3. MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
  4. // 性能分析插件
  5. interceptor.addInnerInterceptor(new IllegalSQLInnerInterceptor());
  6. interceptor.addInnerInterceptor(new SqlExplainInterceptor(
  7. new SqlExplainInterceptor.ExplainStrategy() {
  8. @Override
  9. public String getExplainStrategy() {
  10. return "SERIALIZABLE"; // 执行计划策略
  11. }
  12. }
  13. ));
  14. return interceptor;
  15. }

3. 日志格式优化

推荐使用Logback配置彩色日志:

  1. <configuration>
  2. <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
  3. <encoder>
  4. <pattern>%d{HH:mm:ss.SSS} %highlight(%-5level) %magenta(%logger{36}) - %msg%n</pattern>
  5. </encoder>
  6. </appender>
  7. <logger name="com.baomidou.mybatisplus" level="DEBUG"/>
  8. <root level="INFO">
  9. <appender-ref ref="STDOUT"/>
  10. </root>
  11. </configuration>

4. 执行计划解读

典型SQL日志示例:

  1. 2023-05-20 14:30:45.123 DEBUG c.b.m.c.i.m.MybatisMapperMethod - ==> Preparing: SELECT id,name,age,create_time FROM user WHERE (age = ? AND status = ?) ORDER BY create_time DESC LIMIT ?,?
  2. 2023-05-20 14:30:45.125 DEBUG c.b.m.c.i.m.MybatisMapperMethod - ==> Parameters: 25(Integer), 1(Integer), 0(Integer), 10(Integer)
  3. 2023-05-20 14:30:45.150 DEBUG c.b.m.c.i.m.MybatisMapperMethod - <== Total: 10

执行计划分析要点:

  1. 参数绑定:确认参数值正确传递
  2. SQL重写:验证分页参数是否正确追加
  3. 索引使用:检查是否命中预期索引
  4. 执行时间:关注慢查询(>500ms)

五、常见问题解决方案

1. 分页失效问题

  • 现象:返回所有数据而非分页结果
  • 原因
    • 未正确配置分页插件
    • 查询方法未返回IPage类型
    • 存在其他拦截器冲突
  • 解决方案

    1. // 错误示例(返回List导致分页失效)
    2. public List<User> wrongPageQuery(Page<User> page) {
    3. return userMapper.selectPage(page, null).getRecords(); // 错误方式
    4. }
    5. // 正确示例
    6. public IPage<User> correctPageQuery(Page<User> page) {
    7. return userMapper.selectPage(page, null); // 正确方式
    8. }

2. 条件查询N+1问题

  • 现象:多次执行相似SQL
  • 优化方案

    1. // 优化前(多次查询)
    2. List<User> users = userMapper.selectList(null);
    3. for (User user : users) {
    4. Department dept = deptMapper.selectById(user.getDeptId());
    5. user.setDeptName(dept.getName());
    6. }
    7. // 优化后(使用关联查询)
    8. LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
    9. wrapper.select(User::getId, User::getName, User::getDeptId)
    10. .inSql(User::getDeptId, "SELECT id FROM department WHERE status = 1");
    11. List<User> users = userMapper.selectList(wrapper);

3. SQL日志不输出

  • 检查项
    1. 日志级别是否设置为DEBUG
    2. 是否配置了正确的Logger名称
    3. 是否被其他AOP切面拦截
    4. 数据库驱动是否支持日志输出

六、性能优化建议

  1. 分页参数优化

    • 首页查询建议默认页大小10-20条
    • 深度分页(>1000页)建议使用游标分页
    • 后台任务分页建议设置maxLimit=500
  2. 条件查询优化

    1. // 优化示例:减少OR条件
    2. LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
    3. wrapper.in(User::getStatus, Arrays.asList(1, 2, 3))
    4. .eq(User::getType, 1);
    5. // 替代方案(性能更好):
    6. // SELECT * FROM user WHERE (status = 1 OR status = 2 OR status = 3) AND type = 1
  3. SQL日志分析

    • 重点关注全表扫描警告
    • 检查索引使用情况
    • 分析执行计划中的”Extra”字段

七、进阶功能探索

1. 动态表名支持

  1. @Bean
  2. public MybatisPlusInterceptor dynamicTableNameInterceptor() {
  3. return new MybatisPlusInterceptor()
  4. .addInnerInterceptor(new DynamicTableNameInnerInterceptor() {
  5. @Override
  6. public String dynamicTableName(String sql, String tableName) {
  7. // 根据业务逻辑返回实际表名
  8. return "user_" + LocalDate.now().getYear();
  9. }
  10. });
  11. }

2. 多数据源支持

  1. @Configuration
  2. @MapperScan("com.example.mapper.*")
  3. public class DataSourceConfig {
  4. @Bean
  5. @ConfigurationProperties("spring.datasource.master")
  6. public DataSource masterDataSource() {
  7. return DataSourceBuilder.create().build();
  8. }
  9. @Bean
  10. @ConfigurationProperties("spring.datasource.slave")
  11. public DataSource slaveDataSource() {
  12. return DataSourceBuilder.create().build();
  13. }
  14. @Bean
  15. public SqlSessionFactory sqlSessionFactory() throws Exception {
  16. MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
  17. factory.setDataSource(masterDataSource());
  18. factory.setPlugins(new Interceptor[]{
  19. paginationInnerInterceptor(),
  20. sqlExplainInterceptor()
  21. });
  22. return factory.getObject();
  23. }
  24. }

八、总结与最佳实践

  1. 配置优先级

    • 分页插件 > 性能分析 > 动态表名
    • 日志级别建议开发环境DEBUG,生产环境INFO
  2. 代码规范建议

    1. // 推荐写法(方法职责单一)
    2. public PageResult<UserDTO> queryUsers(UserQueryParam param) {
    3. // 参数校验
    4. validateParam(param);
    5. // 构建查询条件
    6. LambdaQueryWrapper<User> wrapper = buildQueryWrapper(param);
    7. // 执行分页查询
    8. Page<User> page = new Page<>(param.getPageNum(), param.getPageSize());
    9. IPage<User> result = userMapper.selectPage(page, wrapper);
    10. // 结果转换
    11. List<UserDTO> dtos = convertToDTO(result.getRecords());
    12. return new PageResult<>(dtos, result.getTotal());
    13. }
  3. 监控指标

    • 平均分页查询耗时
    • 条件查询命中率
    • SQL执行错误率

通过合理配置MyBatis-Plus的分页插件、条件查询和SQL日志功能,可以显著提升SpringBoot项目的开发效率和运维能力。建议开发团队建立标准的MyBatis-Plus使用规范,定期分析SQL日志,持续优化数据库访问性能。

相关文章推荐

发表评论