Java-15 深入浅出MyBatis 分页与通用 Mapper 实战:PageHelper + tk.mybatis 从配置到分页查询

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

xml 复制代码
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>3.5.1</version>
</dependency>

插件配置

xml 复制代码
<plugin interceptor="com.github.pagehelper.PageHelper">
  <property name="dialect" value="mysql"/>
</plugin>

对应的截图如下所示:

代码实现

java 复制代码
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);
        }
    }
}

执行结果

对应的控制台输出结果如下:

shell 复制代码
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 文件中:

shell 复制代码
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 中增加方法,只要写好实体类,就可以有增删改查方法。

引入依赖

xml 复制代码
<dependency>
    <groupId>tk.mybatis</groupId>
    <artifactId>mapper</artifactId>
    <version>3.5.1</version>
</dependency>

设置主键

我们需要在实体类上设置主键:

java 复制代码
@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:

java 复制代码
public interface UserMapper extends Mapper<WzkUser> {
    // 省略之前的内容
}

编写代码

我们编写代码进行测试:

java 复制代码
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();
    }
}

执行结果

测试运行之后,控制台输出的内容如下所示:

shell 复制代码
WzkUser(id=0, username=wzk2, password=icu2, birthday=Mon Nov 11 00:00:00 CST 2024, orderList=null, roleList=null)

对应的截图如下所示:

编写代码

这里简单说一下 Example 的用法:

java 复制代码
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();
    }
}

执行结果

运行结果之后,控制台的输出结果如下所示:

shell 复制代码
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 SQLcount 永远为 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+ 开启自动下划线转换

作者:武子康的个人博客

相关推荐
学习要积极1 小时前
Spring AI Alibaba-ChatClient
java·人工智能·spring
CodeSheep1 小时前
胡彦斌都开始苦修Vibe Coding,还上架App Store,都卷到编程来了吗?
前端·后端·程序员
z落落1 小时前
C# 虚方法(virtual)与抽象方法 +区别+new方法隐藏 & override方法重写
java·开发语言·c#
宋哥转AI1 小时前
Spring AI Graph:从0到Supervisor(二)并行执行+HITL实战
java·agent
DongWook1 小时前
关于Harness Engineering的一次实践
前端·后端
plainGeekDev1 小时前
XML 布局 → Compose 声明式 UI
android·java·kotlin
PILIPALAPENG1 小时前
gh:终端里的GitHub总控台,AI时代的开发者神器
前端·人工智能·后端
浮游本尊1 小时前
项目全景 + 第一条完整后端链路
java·前端