MyBatis-Plus分页查询实战:从基础到进阶的全流程指南
2025.09.18 16:02浏览量:0简介:本文详细解析MyBatis-Plus分页查询的实现原理,涵盖配置、基础用法、高级技巧及性能优化,提供可落地的代码示例与最佳实践。
一、MyBatis-Plus分页核心机制解析
MyBatis-Plus的分页功能通过PaginationInterceptor
拦截器实现,其核心流程分为三步:
- SQL拦截与改写:拦截原始SQL,在末尾追加
LIMIT offset, size
子句 - 总数统计优化:默认执行
COUNT(1)
查询总记录数,可通过@SqlParser(filter = true)
注解优化 - 结果封装:将查询结果与分页参数封装为
Page<T>
对象
配置示例(Spring Boot环境):
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加分页插件(3.5.1+版本推荐方式)
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
二、基础分页查询实现
1. Service层标准实现
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public Page<User> queryPage(Long current, Long size) {
// 创建分页对象(自动计算offset)
Page<User> page = new Page<>(current, size);
// 执行分页查询(自动封装总数)
return baseMapper.selectPage(page, null);
}
}
2. Controller层标准响应
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/list")
public Result<Page<User>> list(
@RequestParam(defaultValue = "1") Long current,
@RequestParam(defaultValue = "10") Long size) {
Page<User> page = userService.queryPage(current, size);
return Result.success(page);
}
}
响应数据结构:
{
"records": [...], // 当前页数据
"total": 100, // 总记录数
"size": 10, // 每页大小
"current": 1, // 当前页码
"pages": 10 // 总页数
}
三、进阶功能实现
1. 条件分页查询
public Page<User> queryByCondition(Long current, Long size, String name) {
Page<User> page = new Page<>(current, size);
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.like("name", name); // 添加模糊查询条件
return baseMapper.selectPage(page, wrapper);
}
2. 自定义SQL分页
对于复杂SQL,推荐使用XML方式:
<!-- UserMapper.xml -->
<select id="selectCustomPage" resultType="User">
SELECT * FROM user
WHERE age > #{age}
ORDER BY create_time DESC
</select>
Service层调用:
public Page<User> customQueryPage(Long current, Long size, Integer age) {
Page<User> page = new Page<>(current, size);
// 手动设置SQL参数
Map<String, Object> params = new HashMap<>();
params.put("age", age);
// 执行分页查询(需配合@Select注解使用)
IPage<User> result = userMapper.selectCustomPage(page, params);
return (Page<User>) result;
}
3. 多表关联分页
对于关联查询,建议采用两种方案:
- DTO封装方案:
```java
public class UserWithRoleDTO {
private User user;
private Role role;
// getters/setters
}
public Page
Page
List
return users.stream().map(user -> {
UserWithRoleDTO dto = new UserWithRoleDTO();
dto.setUser(user);
// 查询关联角色(实际开发中建议批量查询优化)
Role role = roleService.getByUserId(user.getId());
dto.setRole(role);
return dto;
}).collect(Collectors.toCollection(Page::new)); // 需自定义Page转换
}
2. **MyBatis关联查询方案**(需XML配置):
```xml
<select id="selectUserWithRole" resultMap="userWithRoleMap">
SELECT u.*, r.name as role_name
FROM user u LEFT JOIN user_role ur ON u.id = ur.user_id
LEFT JOIN role r ON ur.role_id = r.id
</select>
四、性能优化实践
1. 计数查询优化
对于大数据量表,建议:
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
// 关闭自动COUNT(需手动处理总数)
paginationInnerInterceptor.setMaxLimit(-1L);
interceptor.addInnerInterceptor(paginationInnerInterceptor);
return interceptor;
}
替代方案:使用缓存总数
@Cacheable(value = "userCount", key = "#root.methodName")
public Long getUserCount() {
return baseMapper.selectCount(null);
}
2. 数据库索引优化
建议为分页查询字段建立复合索引:
ALTER TABLE user ADD INDEX idx_name_age (name, age);
3. 深度分页优化
对于深度分页(如第1000页),建议使用子查询优化:
public Page<User> deepPageQuery(Long current, Long size) {
Page<User> page = new Page<>(current, size);
// 计算offset
long offset = (current - 1) * size;
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.last("LIMIT " + offset + "," + size); // 3.5.1+版本慎用
// 更推荐使用子查询方式
return baseMapper.selectPage(page, wrapper);
}
五、常见问题解决方案
1. 分页失效问题排查
- 检查是否正确配置
PaginationInterceptor
- 确认Mapper接口是否继承
BaseMapper<T>
- 检查SQL是否包含
ORDER BY
(可能导致总数统计异常)
2. 总数统计错误处理
@Override
public Page<User> safeQueryPage(Long current, Long size) {
Page<User> page = new Page<>(current, size);
try {
return baseMapper.selectPage(page, null);
} catch (Exception e) {
// 总数统计失败时降级处理
page.setTotal(0L);
List<User> records = baseMapper.selectList(null);
// 手动分页(仅适用于小数据量)
int fromIndex = (int) ((current - 1) * size);
int toIndex = Math.min(fromIndex + size.intValue(), records.size());
page.setRecords(records.subList(fromIndex, toIndex));
return page;
}
}
3. 多数据源分页配置
对于多数据源场景,需分别配置:
@Bean
@ConfigurationProperties("spring.datasource.master")
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public SqlSessionFactory masterSqlSessionFactory() throws Exception {
MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
factory.setDataSource(masterDataSource());
factory.setPlugins(new Interceptor[]{
new PaginationInnerInterceptor(DbType.MYSQL)
});
return factory.getObject();
}
六、最佳实践建议
- 分页大小限制:建议前端限制最大分页大小为1000条
- 默认分页参数:Controller层应设置合理的默认值
- 结果缓存:对不常变动的分页数据添加缓存
- 异步加载:对于大数据量分页,考虑前端懒加载
- 监控告警:对深度分页查询添加监控日志
七、版本兼容说明
MyBatis-Plus版本 | 分页插件配置方式 | 注意事项 |
---|---|---|
3.4.x及以下 | @Bean public PaginationInterceptor paginationInterceptor() |
已废弃 |
3.5.0+ | MybatisPlusInterceptor + PaginationInnerInterceptor |
推荐方式 |
最新版 | 支持多数据源分页 | 需显式指定DbType |
本文通过12个核心代码示例、5个优化方案和3个问题解决方案,系统阐述了MyBatis-Plus分页查询的实现要点。实际开发中,建议结合项目特点选择适合的分页策略,并持续监控分页查询性能。对于超大数据量场景,可考虑采用Elasticsearch等搜索引擎实现分页功能。
发表评论
登录后可评论,请前往 登录 或 注册