TL;DR
- 场景:在 MyBatis / MyBatis-Plus 项目中,需要对单表与多表查询做物理分页,并减少单表 CRUD 的样板 SQL。
- 结论:
PageHelper通过拦截器改写 SQL 实现自动分页;tk.mybatis通用 Mapper 通过继承Mapper<T>自动获得单表 CRUD 方法,两者可叠加使用。 - 产出:可直接复用的 POM 依赖、MyBatis 插件配置、Spring 配置、
PageHelper.startPage调用模板、PageInfo字段速查、Example 条件构造器示例,以及 5 类典型错误速查卡。
版本矩阵
| 功能 / 组件 | 状态 | 说明 |
|---|---|---|
| PageHelper 分页(com.github.pagehelper:pagehelper) | ✅ 已验证 | 最新稳定版 6.1.1(2025-06-20),支持 MyBatis 3.4+;本文示例使用的 3.5.1 严重过时,存在已知安全与兼容问题,建议升级 |
| PageHelper Spring Boot 集成 | ✅ 已验证 | 引入 pagehelper-spring-boot-starter,通过 application.yml 配置 helper-dialect |
| tk.mybatis 通用 Mapper(tk.mybatis:mapper) | ✅ 已验证 | 最新稳定版 6.0.0(2026-02-12),支持 JDK 8+;本文示例 3.5.1 同样过时 |
| tk.mybatis Spring Boot Starter | ✅ 已验证 | tk.mybatis:mapper-spring-boot-starter,启动类需使用 tk.mybatis.spring.annotation.MapperScan |
PageInfo 分页结果封装 | ✅ 已验证 | getTotal/getPages/getPageNum/getPageSize/getList 字段稳定 |
Example 条件构造器 | ✅ 已验证 | Example.createCriteria().andEqualTo/andGreaterThan/andLike… |
核查说明:PageHelper 6.1.1 来源于 github.com/pagehelper/… 官方发布;tk.mybatis 6.0.0 来源于 github.com/abel533/Map… 官方发布,2026-06-04 当下推荐。
PageHelper
MyBatis 使用第三方插件对功能进行扩展,分页助手 PageHelper 是将分页的复杂操作进行封装,使用简单的方式即可获得分页的相关数据。PageHelper 是一个用于 MyBatis 或 MyBatis-Plus 的分页插件,它提供了简单易用的分页功能。它通过拦截 SQL 查询,在查询时自动添加分页条件,从而实现数据的分页查询。PageHelper 的主要优点是简单易用,并且可以兼容大部分的数据库和 MyBatis 版本。
基本功能
PageHelper 可以帮助我们在使用 MyBatis 时自动进行分页查询,避免手动编写复杂的分页逻辑。它通过拦截器的方式,在执行查询时动态地将分页参数传入 SQL 语句,并通过设置分页对象来获取分页结果。
工作原理
PageHelper 的核心功能是通过拦截器修改 SQL,添加分页的参数。它在查询执行前,会根据提供的分页参数修改原始的 SQL 查询,添加 LIMIT、OFFSET 等分页条件,然后返回分页结果。分页信息通过 Page 类提供,分页对象封装了总记录数、总页数等信息。
开发步骤
- 导入通用 PageHelper 的坐标
- 在 MyBatis 核心配置文件中配置 PageHelper 插件
- 测试分页数据获取
导入pom
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>3.5.1</version>
</dependency>
插件配置
<plugin interceptor="com.github.pagehelper.PageHelper">
<property name="dialect" value="mysql"/>
</plugin>
对应的截图如下所示:
代码实现
public class WzkicuPage01 {
public static void main(String[] args) throws Exception {
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
PageHelper.startPage(2, 1);
List<WzkUser> dataList = userMapper.findAll();
for (WzkUser wzk : dataList) {
System.out.println(wzk);
}
}
}
执行结果
对应的控制台输出结果如下:
WzkUser(id=2, username=wzk2, password=icu2, birthday=Mon Nov 11 00:00:00 CST 2024, orderList=null, roleList=null)
对应的截图如下所示:
配置 PageHelper
在 Spring 配置文件中配置 PageHelper 插件,通常是在 application.properties 或 application.yml 文件中:
pagehelper.helperDialect=mysql # 配置数据库方言,如 mysql、oracle、postgresql 等
pagehelper.reasonable=true # 启用合理化分页
pagehelper.supportMethodsArguments=true # 支持通过方法参数传递分页参数
PageInfo类
PageInfo 是 PageHelper 用于封装分页查询结果的一个重要类。它包含了分页相关的信息:
- getTotal():总记录数。
- getPages():总页数。
- getPageNum():当前页码。
- getPageSize():每页大小。
- getList():当前页的数据。
注意事项
- 在使用 PageHelper 时,分页查询必须放在调用 PageHelper.startPage() 之后,执行查询之前。
- 如果使用了 @Mapper 注解的 Mapper 接口,PageHelper 可以自动与 MyBatis 配合工作。
- 对于需要分页的查询,返回的结果必须是一个 List 类型,如果返回的是其他类型的数据结构,PageHelper 可能无法正确分页。
通用 mapper
简单介绍
通用 mapper 就是为了解决单表的增删改查,基于 MyBatis 的插件机制,开发人员不需要编写 SQL,不需要在 DAO 中增加方法,只要写好实体类,就可以有增删改查方法。
引入依赖
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper</artifactId>
<version>3.5.1</version>
</dependency>
设置主键
我们需要在实体类上设置主键:
@Table(name = "wzk_user")
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class WzkUser implements Serializable {
private int id;
private String username;
private String password;
private Date birthday;
private List<WzkOrder> orderList;
private List<WzkRole> roleList;
}
对应的代码如下所示:
修改DAO
我们需要在 Mapper 上继承 Mapper:
public interface UserMapper extends Mapper<WzkUser> {
// 省略之前的内容
}
编写代码
我们编写代码进行测试:
public class WzkMapper01 {
public static void main(String[] args) throws Exception {
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
MapperHelper mapperHelper = new MapperHelper();
mapperHelper.registerMapper(UserMapper.class);
mapperHelper.processConfiguration(sqlSession.getConfiguration());
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<WzkUser> dataList = userMapper.selectAll();
for (WzkUser each : dataList) {
System.out.println(each);
}
sqlSession.close();
}
}
执行结果
测试运行之后,控制台输出的内容如下所示:
WzkUser(id=0, username=wzk2, password=icu2, birthday=Mon Nov 11 00:00:00 CST 2024, orderList=null, roleList=null)
对应的截图如下所示:
编写代码
这里简单说一下 Example 的用法:
public class WzkMapper02 {
public static void main(String[] args) throws Exception {
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
MapperHelper mapperHelper = new MapperHelper();
mapperHelper.registerMapper(UserMapper.class);
mapperHelper.processConfiguration(sqlSession.getConfiguration());
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
Example example = new Example(WzkUser.class);
example.createCriteria().andEqualTo("username", "wzk2");
List<WzkUser> dataList = userMapper.selectByExample(example);
for (WzkUser each : dataList) {
System.out.println(each);
}
sqlSession.close();
}
}
执行结果
运行结果之后,控制台的输出结果如下所示:
WzkUser(id=0, username=wzk2, password=icu2, birthday=Mon Nov 11 00:00:00 CST 2024, orderList=null, roleList=null)
对应的截图如下所示:
错误速查卡
| 症状 | 根因 | 定位 | 修复 |
|---|---|---|---|
startPage 紧接的查询没有分页,SQL 中没有 LIMIT | startPage 与 mapper 调用之间插入了 System.out.println、判空、日志等逻辑,ThreadLocal 中的分页参数被消费 | 开启 MyBatis DEBUG 日志,确认 SQL 是否带 limit ?,? | 保证 PageHelper.startPage(pageNum, pageSize) 与 mapper 查询紧邻,中间不要插入任何业务代码 |
SELECT … LIMIT … 后多了 ; 导致 SQL 报错 | PageHelper 基于 JSqlParser 解析 SQL,末尾的分号会让分页解析失败 | 报错 Expected end of SQL 或 count 永远为 0 | 移除 mapper.xml 中 ; 结尾,或升级 PageHelper 至 5.x+(5.x 兼容分号) |
全部数据返回 / total 远大于实际 | dialect 与数据库不匹配(如 MySQL 库误配 oracle) | 查看 PageHelper 启动日志中的 dialect | 改为对应数据库:mysql/oracle/postgresql/sqlserver 等,或升级后使用 helper-dialect 自动探测 |
启动报 tk.mybatis.mapper.common.Mapper that could not be found | @MapperScan 用成了 org.mybatis.spring.annotation.MapperScan | 启动日志中查找 BeanCreationException | 切换为 import tk.mybatis.spring.annotation.MapperScan; |
selectAll 输出对象 id=0 但库内 id 有值 | 通用 Mapper 的 selectAll 自动生成的 SQL 只查询非主键字段(username,password,birthday),主键未映射 | 开启日志确认 SQL 是否包含 id 列 | 改用 select(WzkUser.class) 显式指定查询字段,或在实体 id 字段加 @Column 显式映射;或直接用 selectByPrimaryKey 查单条 |
Example 条件字段与数据库列名不一致,条件不生效 | Example.createCriteria().andEqualTo("userName", …) 用了驼峰字段名而非数据库列名 | DEBUG 日志看 WHERE 子句 | 改为数据库列名 andEqualTo("user_name", …),或升级 tk.mybatis 至 4.x+ 开启自动下划线转换 |
作者:武子康的个人博客