Spring Boot集成PageHelper:轻松实现数据库分页功能
1. 为什么需要分页?
分页是处理大数据量查询的核心技术,其重要性体现在:
- 性能优化:避免单次查询返回过多数据导致内存溢出或响应延迟。
- 用户体验:前端展示分页导航,用户可快速定位目标数据。
- 网络开销:减少不必要的数据传输,节省带宽。
传统分页的痛点:
- 复杂SQL:需手动编写
LIMIT
、OFFSET
等分页语句,尤其多表联查时易出错。 - 维护困难:分页逻辑散落在多个DAO层方法中,修改分页规则需全局调整。
2. PageHelper简介
PageHelper是MyBatis的物理分页插件,核心功能包括:
- 自动分页:拦截SQL并动态添加分页语句,无需修改原查询逻辑。
- 多数据库支持:自动识别MySQL、Oracle等方言,生成对应分页语法。
- 无缝集成:与Spring Boot和MyBatis深度整合,仅需简单配置即可使用。
优势对比:
方案 | 代码量 | 可维护性 | 跨数据库支持 |
---|---|---|---|
手写SQL分页 | 多 | 差 | 无 |
PageHelper | 少 | 优 | 有 |
Spring Data JPA | 中等 | 良 | 有 |
3. 环境准备
步骤1:创建Spring Boot项目
使用 Spring Initializr 生成项目,勾选:
- MyBatis Framework
- MySQL Driver(或其他数据库驱动)
步骤2:添加PageHelper依赖
<!-- Maven -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.7</version>
</dependency>
步骤3:配置数据源与分页参数
# application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/demo?useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
mapper-locations: classpath:mapper/*.xml
pagehelper:
helperDialect: mysql # 指定数据库方言
reasonable: true # 页码越界自动修正(如pageNum=100时返回最后一页)
supportMethodsArguments: true # 支持通过方法参数传递分页条件
4. 核心实现
4.1 创建实体类和Mapper接口
// User.java
@Data
public class User {
private Long id;
private String name;
private String email;
}
// UserMapper.java
@Mapper
public interface UserMapper {
List<User> selectAllUsers();
}
4.2 编写Mapper XML文件
<!-- resources/mapper/UserMapper.xml -->
<mapper namespace="com.example.mapper.UserMapper">
<select id="selectAllUsers" resultType="User">
SELECT id, name, email FROM user
</select>
</mapper>
4.3 Service层实现分页查询
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public PageInfo<User> getUsers(int pageNum, int pageSize) {
// 关键:调用startPage后第一个查询自动分页
PageHelper.startPage(pageNum, pageSize);
List<User> users = userMapper.selectAllUsers();
return new PageInfo<>(users);
}
}
4.4 Controller层暴露API
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@GetMapping
public ResponseEntity<PageInfo<User>> listUsers(
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size) {
PageInfo<User> pageInfo = userService.getUsers(page, size);
return ResponseEntity.ok(pageInfo);
}
}
4.5 分页结果示例
请求 GET /api/users?page=2&size=5
返回:
{
"total": 50,
"pageNum": 2,
"pageSize": 5,
"pages": 10,
"list": [
{"id": 6, "name": "User6", "email": "user6@example.com"},
...
]
}
5. 高级配置
5.1 自定义分页参数
pagehelper:
params: count=countSql # 将COUNT查询转换为COUNT_SQL优化语句
page-size-zero: true # 允许pageSize=0时返回全部结果
max-page-size: 100 # 限制每页最大数据量
5.2 多数据源分页
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource.db1")
public DataSource db1DataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public SqlSessionFactory db1SqlSessionFactory() throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(db1DataSource());
// 配置PageHelper插件
PageInterceptor pageInterceptor = new PageInterceptor();
Properties props = new Properties();
props.setProperty("helperDialect", "mysql");
pageInterceptor.setProperties(props);
factory.setPlugins(pageInterceptor);
return factory.getObject();
}
}
5.3 与Spring Data JPA结合
- 适用场景:同时需要JPA的便捷CRUD和复杂SQL分页。
- 实现方式:在JPA Repository中注入MyBatis Mapper,混合使用。
6. 常见问题与解决方案
问题1:分页失效
- 排查步骤:
- 确认
PageHelper.startPage()
在查询前调用。 - 检查是否配置了多个MyBatis插件导致拦截顺序冲突。
- 确认
问题2:SQL注入风险
- 防御措施:
- 避免直接拼接SQL参数,如
ORDER BY ${sortField}
。 - 使用
PageHelper
的orderBy
方法安全排序:PageHelper.startPage(1, 10).setOrderBy("id desc");
- 避免直接拼接SQL参数,如
问题3:分页结果不准确
- 原因:
- 查询包含
GROUP BY
或子查询时,自动生成的COUNT语句可能错误。
- 查询包含
- 解决:手动指定COUNT查询:
<select id="selectAllUsers" resultType="User"> SELECT id, name FROM user </select> <select id="selectAllUsers_COUNT" resultType="Long"> SELECT COUNT(1) FROM user </select>
7. 总结与扩展
适用场景:
- 后台管理系统数据表格展示
- 移动端APP的分页加载
- 大数据量报表分批处理
扩展学习:
- PageHelper官方文档
- MyBatis动态SQL技巧
- 实战案例:电商订单分页查询
流程图:PageHelper分页流程
避坑指南:
- 索引优化:确保分页字段(如
id
)有索引,避免OFFSET
过大时性能下降。 - 参数校验:校验
pageNum
和pageSize
的合法性,防止负数或超大值。 - 线程安全:
PageHelper.startPage()
基于ThreadLocal,需注意异步场景下的数据隔离。
通过本文,您已掌握Spring Boot集成PageHelper的核心技巧。立即实践,让分页功能从此高效又优雅! 🚀