Hey, Nice to meet You. 

有过人之节.人情有所不能忍者,匹夫见辱,拔剑而起,挺身而斗,此不足为勇也,天下有大勇者,猝然临之而不惊,无故加之而不怒.此其所挟持者甚大,而其志甚远也.          ☆☆☆所谓豪杰之士,必

Mybatis3详解(十四)----Mybatis的分页

 


1、前言

在前面学习mybatis的时候,会经常对数据进行增删改查操作,使用最多的是对数据库进行查询操作,但是前面都是简单的案例,所以查询的数据量不是很大,自然查询时没有任何压力,但是如果在实际的项目中,数据库的数据成千上万,如果还是这样一次性查询出所有数据,那么会导致数据可读性和数据库性能极差。所以我们往往使用分页进行查询,这样对数据库压力就在可控范围内。

这里介绍Mybatis的这几种分页方式:

  1. 原生SQL的Limit分页
  2. Mybatis自带的RowBounds分页
  3. 自定义拦截器插件进行分页
  4. 使用PageHelper插件分页

下面我们来简单学习一下。

2、原生Limit分页

原生Limit分页就是在编写sql语句时需要自己加上limit关键字,然后传入分页参数进行分页,例如select * from t_user limit 0,3;

①、编写UserMapper接口

01
02
03
04
05
06
07
/**
 * UserMapper接口
 */
public interface UserMapper {
    //分页查询所有用户,通过原生limit
    List<User> selectAllUserByLimit(Map map);
}

②、UserMapper.xml映射文件

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.thr.mapper.UserMapper">
     
    <resultMap id="userMap" type="com.thr.pojo.User">
        <id property="userId" column="id"/>
        <result property="userName" column="username"/>
        <result property="userAge" column="age"/>
        <result property="userBirthday" column="birthday"/>
        <result property="userSex" column="sex"/>
        <result property="userAddress" column="address"/>
    </resultMap>
 
    <!-- 分页查询所有用户,通过原生limit -->
    <select id="selectAllUserByLimit" resultMap="userMap">
        select * from t_user limit #{start},#{size}
    </select>
</mapper>

③、测试分页方法

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//Mybatis的测试
public class MybatisTest2 {
    //定义 SqlSession
    private SqlSession sqlSession = null;
    //定义 UserMapper对象
    private UserMapper mapper = null;
 
    @Before//在测试方法执行之前执行
    public void getSqlSession(){
        //1、加载 mybatis 全局配置文件
        InputStream is = MybatisTest2.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
        //2、创建SqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        //3、根据 sqlSessionFactory 产生session
        sqlSession = sqlSessionFactory.openSession();
        //4、创建Mapper接口的的代理对象,getMapper方法底层会通过动态代理生成UserMapper的代理实现类
        mapper = sqlSession.getMapper(UserMapper.class);
    }
 
    @After//在测试方法执行完成之后执行
    public void destroy() throws IOException {
        sqlSession.commit();
        sqlSession.close();
    }
    //分页查询所有用户信息,通过原生limit
    @Test
    public void selectAllUserByLimit(){
        int currPage = 2;//当前页码
        int pageSize = 3;//当前显示页记录数量
        HashMap<String, Object> map = new HashMap<>();
        //计算起始位置,注意:currPage和start别搞错了,一个表示当前页码,一个是从第几行读取记录
        map.put("start",(currPage-1)*pageSize);
        //页面显示记录数
        map.put("size",pageSize);
        System.out.println("当前页码为:第"+currPage+"页,页面显示记录数量:"+pageSize+"个");
        List<User> userList = mapper.selectAllUserByLimit(map);
        for (User user : userList) {
            System.out.println(user);
        }
    }
}

④、运行结果

image

3、RowBounds分页

Mybatis内置了一个专门处理分页的类——RowBounds,我们使用它可以轻松完成分页。

RowBounds源代码如下:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package org.apache.ibatis.session;
 
public class RowBounds {
    //默认值为0~~Java最大整数
    public static final int NO_ROW_OFFSET = 0;
    public static final int NO_ROW_LIMIT = Integer.MAX_VALUE;
    public static final RowBounds DEFAULT = new RowBounds();
    //偏移量,即从第几行开始读取
    private final int offset;
    //限制,即每页显示记录数量
    private final int limit;
 
    public RowBounds() {
        this.offset = NO_ROW_OFFSET;
        this.limit = NO_ROW_LIMIT;
    }
    public RowBounds(int offset, int limit) {
        this.offset = offset;
        this.limit = limit;
    }
    public int getOffset() {
        return offset;
    }
    public int getLimit() {
        return limit;
    }
}

那么我们怎样来使用这个RowBounds分页呢?非常的简单。

①、定义接口方法

01
02
//分页查询所有用户,通过自带的RowBounds
List<User> selectAllUserByRowBounds(RowBounds rowBounds);

②、sql映射

01
02
03
04
<!-- 分页查询所有用户,通过自带的RowBounds -->
<select id="selectAllUserByRowBounds" resultMap="userMap">
    select * from t_user
</select>

使用RowBounds分页我们可以不写在映射SQL中写limit关键字,到时候自动回给我们拼接。就两个字,方便!

③、测试方法

01
02
03
04
05
06
07
08
09
10
11
12
13
//分页查询所有用户信息,通过自带的RowBounds
@Test
public void selectAllUserByRowBounds(){
    int currPage=2;//当前页码
    int pageSize=3;//当前页显示记录数量
    //注意:currPage和start别搞错了,一个表示当前页码,一个是从第几行读取记录
    int start = (currPage-1)*pageSize;//计算从第几行读取记录
    RowBounds rowBounds = new RowBounds(start,pageSize);
    List<User> userList = mapper.selectAllUserByRowBounds(rowBounds);
    for (User user : userList) {
        System.out.println(user);
    }
}

④、运行结果

image

RowBounds分页有一点好处就是处理数据量少时还可以,但是数据量大时,就不行好用了,此时一般都会实现拦截器来完成分页。

4、自定义拦截器插件分页

自定义拦截器插件分页 需要自己定义一个类实现Interceptor接口,这个接口是Mybatis提供的。任何分页插件想要对Mybatis进行分页就必须实现Interceptor接口,包括后面PageHelper分页插件。

①、创建MyPageInterceptor类

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
/**
 * @Intercepts 表示是一个拦截器
 * @Signature 拦截器的签名
 * type 拦截的类型 四大对象之一( Executor,ResultSetHandler,ParameterHandler,StatementHandler)
 * method 拦截的方法
 */
@Intercepts({@Signature(type=StatementHandler.class,method="prepare",args={Connection.class, Integer.class })})
public class MyPageInterceptor implements Interceptor {
 
    //当前页码
    private int currPage;
    //每页显示的条目数
    private int pageSize;
    //数据库类型
    private String dbType;
 
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        System.out.println("plugin is running...");
        //获取StatementHandler,默认是RoutingStatementHandler
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        //获取statementHandler包装类
        MetaObject MetaObjectHandler = SystemMetaObject.forObject(statementHandler);
 
        //分离代理对象链
        while (MetaObjectHandler.hasGetter("h")) {
            Object obj = MetaObjectHandler.getValue("h");
            MetaObjectHandler = SystemMetaObject.forObject(obj);
        }
 
        while (MetaObjectHandler.hasGetter("target")) {
            Object obj = MetaObjectHandler.getValue("target");
            MetaObjectHandler = SystemMetaObject.forObject(obj);
        }
 
        //获取连接对象
        //Connection connection = (Connection) invocation.getArgs()[0];
        //object.getValue("delegate");  获取StatementHandler的实现类
 
        //获取查询接口映射的相关信息
        MappedStatement mappedStatement = (MappedStatement) MetaObjectHandler.getValue("delegate.mappedStatement");
        String mapId = mappedStatement.getId();
 
        //statementHandler.getBoundSql().getParameterObject();
 
        //拦截以.ByPage结尾的请求,分页功能的统一实现
        if (mapId.matches(".+ByPage$")) {
            //获取进行数据库操作时管理参数的handler
            ParameterHandler parameterHandler = (ParameterHandler) MetaObjectHandler.getValue("delegate.parameterHandler");
            //获取请求时的参数
            Map<String, Object> paraObject = (Map<String, Object>) parameterHandler.getParameterObject();
            //也可以这样获取
            //paraObject = (Map<String, Object>) statementHandler.getBoundSql().getParameterObject();
 
            //参数名称和在service中设置到map中的名称一致
            currPage = (int) paraObject.get("currPage");
            pageSize = (int) paraObject.get("pageSize");
 
            String sql = (String) MetaObjectHandler.getValue("delegate.boundSql.sql");
            //也可以通过statementHandler直接获取
            //sql = statementHandler.getBoundSql().getSql();
 
            //构建分页功能的sql语句
            String limitSql;
            sql = sql.trim();
            limitSql = sql + " limit " + (currPage - 1) * pageSize + "," + pageSize;
 
            //将构建完成的分页sql语句赋值个体'delegate.boundSql.sql',偷天换日
            MetaObjectHandler.setValue("delegate.boundSql.sql", limitSql);
        }
        //调用原对象的方法,进入责任链的下一级
        return invocation.proceed();
    }
 
    //获取代理对象
    @Override
    public Object plugin(Object o) {
        //生成object对象的动态代理对象
        return Plugin.wrap(o, this);
    }
 
    //设置代理对象的参数
    @Override
    public void setProperties(Properties properties) {
        //如果项目中分页的pageSize是统一的,也可以在这里统一配置和获取,这样就不用每次请求都传递pageSize参数了。参数是在配置拦截器时配置的。
        String limit1 = properties.getProperty("limit", "10");
        this.pageSize = Integer.valueOf(limit1);
        this.dbType = properties.getProperty("dbType", "mysql");
    }
}

②、全局配置文件增加plugin设置(注意位置)

01
02
03
04
05
<!-- 配置自定义分页插件 -->
<plugins>
    <plugin interceptor="com.thr.interceptor.MyPageInterceptor">
    </plugin>
</plugins>

③、接口方法

01
02
//分页查询所有用户,通过原生自定义拦截器
List<User> selectAllUserByPage(Map map);

由于拦截器中设置了拦截以.ByPage结尾的方法,所以方法一定要命名正确,

④、sql映射

01
02
03
04
<!-- 分页查询所有用户,通过自定义拦截器 -->
<select id="selectAllUserByPage" resultMap="userMap">
    select * from t_user
</select>

⑤、测试方法

image

5、PageHelper分页插件

PageHelper是一款非常优秀的分页插件,用的人非常多,详细的可以参考PageHelper的官方文档,讲的比较通俗易懂。链接:https://pagehelper.github.io/docs/howtouse/。 PageHelper分页其实也是自定义拦截器方式的一种第三方实现,它内部帮助我们实现了Interceptor的功能。所以实际上我们在执行查询方法之前,PageHelper分页插件同样是对我们的 sql 进行拦截,然后对分页参数进行拼接。

PageHelper的简单使用:

①、引入PageHelper依赖:

01
02
03
04
05
06
<!-- pagehelper分页插件 -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.2.0</version>
</dependency>

②、全局配置文件增加plugin设置(注意位置)

01
02
03
04
05
<!-- 配置分页插件 -->
<plugins>
    <!-- PageHelper5版本配置 -->
    <plugin interceptor="com.github.pagehelper.PageInterceptor"/>
</plugins>

③、接口方法

01
02
//分页查询所有用户,通过PageHelper
List<User> selectAllUserByPageHelper();

④、sql映射

01
02
03
04
<!-- 分页查询所有用户,通过PageHelper -->
<select id="selectAllUserByPageHelper" resultMap="userMap">
    select * from t_user
</select>

⑤、测试方法

01
02
03
04
05
06
07
08
09
10
11
12
//分页查询所有用户信息,通过PageHelper
@Test
public void selectAllUserByPageHelper(){
    int currPage = 2;//当前页码
    int pageSize = 3;//当前页记录数量
    //表示获取第2页,3条内容,默认会查询总数count
    PageHelper.startPage(currPage,pageSize);
    List<User> userList = mapper.selectAllUserByPageHelper();
    for (User user : userList) {
        System.out.println(user);
    }
}

⑥、运行结果

image

以上只是PageHelper的简单介绍,还有更多的功能可以去参考官方文档,也可以自行百度学习。

posted @   唐浩荣  阅读(9728)  评论(2编辑  收藏  举报
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
点击右上角即可分享
微信分享提示

目录导航