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接口

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

②、UserMapper.xml映射文件

<?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>

③、测试分页方法

//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);
}
}
}

④、运行结果

3、RowBounds分页

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

RowBounds源代码如下:

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分页呢?非常的简单。

①、定义接口方法

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

②、sql映射

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

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

③、测试方法

    //分页查询所有用户信息,通过自带的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);
}
}

④、运行结果

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

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

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

①、创建MyPageInterceptor类

/**
* @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设置(注意位置)

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

③、接口方法

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

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

④、sql映射

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

⑤、测试方法

5、PageHelper分页插件

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

PageHelper的简单使用:

①、引入PageHelper依赖:

        <!-- pagehelper分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>

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

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

③、接口方法

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

④、sql映射

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

⑤、测试方法

    //分页查询所有用户信息,通过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);
}
}

⑥、运行结果

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

Mybatis3详解(十四)----Mybatis的分页的更多相关文章

  1. Linux学习之用户配置文件详解(十四)

    Linux学习之用户配置文件详解 目录 用户信息文件/etc/password 影子文件/etc/shadow 组信息文件/etc/group 组密码文件/etc/gshadow 用户信息文件/etc ...

  2. Nginx详解十四:Nginx场景实践篇之代理服务

    代理的作用 Nginx代理 正向代理 反向代理 正向代理和反向代理的区别:代理的对象不一样 正向代理代理的对象是客户端,反向代理代理的对象是服务端 反向代理: 配置语法:proxy_pass URL; ...

  3. Mybatis源码详解系列(四)--你不知道的Mybatis用法和细节

    简介 这是 Mybatis 系列博客的第四篇,我本来打算详细讲解 mybatis 的配置.映射器.动态 sql 等,但Mybatis官方中文文档对这部分内容的介绍已经足够详细了,有需要的可以直接参考. ...

  4. ViewPager 详解(四)----自主实现滑动指示条

    前言:前面我们用了三篇的时间讲述了有关ViewPager的基础知识,到这篇就要进入点实际的了.在第三篇<ViewPager 详解(三)---PagerTabStrip与PagerTitleStr ...

  5. spring事务详解(四)测试验证

    系列目录 spring事务详解(一)初探事务 spring事务详解(二)简单样例 spring事务详解(三)源码详解 spring事务详解(四)测试验证 spring事务详解(五)总结提高 一.引子 ...

  6. RocketMQ详解(四)核心设计原理

    专题目录 RocketMQ详解(一)原理概览 RocketMQ详解(二)安装使用详解 RocketMQ详解(三)启动运行原理 RocketMQ详解(四)核心设计原理 RocketMQ详解(五)总结提高 ...

  7. OutputCache属性详解(四)— SqlDependency

    目录 OutputCache概念学习 OutputCache属性详解(一) OutputCache属性详解(二) OutputCache属性详解(三) OutputCache属性详解(四)— SqlD ...

  8. IIS负载均衡-Application Request Route详解第四篇:使用ARR实现三层部署架构(转载)

    IIS负载均衡-Application Request Route详解第四篇:使用ARR实现三层部署架构 系列文章链接: IIS负载均衡-Application Request Route详解第一篇: ...

  9. 前端技术之_CSS详解第四天

    前端技术之_CSS详解第四天 一.第三天的小总结 盒模型box model,什么是盒子? 所有的标签都是盒子.无论是div.span.a都是盒子.图片.表单元素一律看做文本. 盒模型有哪些组成: wi ...

  10. Keepalived详解(四):通过vrrp_script实现对集群资源的监控【转】

    一.通过vrrp_script实现对集群资源的监控: Keepalived基础HA功能时用到了vrrp_script这个模块,此模块专门用于对集群中服务资源进行监控.与此模块一起使用的还有track_ ...

随机推荐

  1. Bug Hunt UVA - 1596

      In this problem, we consider a simple programming language that has only declarations of onedimens ...

  2. 2020北航OO第三单元总结

    2020北航OO第三单元总结 本单元要求是根据JML规格完善代码,初看是一个简单的代码照搬实现的东西,但最后才发现由于CPU时间的限制,还考察了大量优化策略及数据结构中关于图的知识,是一次非常注重细节 ...

  3. PAT归纳总结——关于二叉树的一些总结

    今天是6月26日到下个月的这个时候已经考过试了,为了让自己考一个更高的分数,所以我打算把PAT的相关题型做一个总结.目前想到的方法就是将相关的题型整理到一起然后,针对这种题型整理出一些方法. 二叉树的 ...

  4. lumen Rest API 起步

    lumen Rest API 起步 修改项目文件 .env DB_DATABASE=<数据库名> DB_USERNAME=<数据库用户名> DB_PASSWORD=<数据 ...

  5. 路由器逆向分析------MIPS交叉编译环境的搭建(Buildroot)

    本文博客地址:http://blog.csdn.net/qq1084283172/article/details/68950682 为了能在我们熟悉的windows或者ubuntu下开发mips架构的 ...

  6. POJ2669不错的最大流 竞赛问题(枚举King的个数)

    题意:       有n个人,任意两个人都比一次赛(一共比了n*(n-1)/2场),赢一场得到一分,最后的时候如果得分最高,或者是自己打败了所有比自己得分都高的人就算是King,给你每个人的最后得分, ...

  7. URL分发器(视图层)

    目录 视图 URL映射 path函数 URL中传入参数 普通传入参数 变量形式传入参数 ​URL中包含另一个urls模块 URL命名.URL反转.应用命名空间 视图 视图一般都写在 app  的 vi ...

  8. 逆向 string.h 函数库 memset、strcpy、strcmp 函数

    memset 函数 函数原型:void *memset(void *str, int c, size_t n) 主要功能:复制字符 c(一个无符号字符)到参数 str 所指向的字符串的前 n 个字符 ...

  9. Python技术栈性能测试工具Locust入门

    Locust是一款Python技术栈的开源的性能测试工具.Locust直译为蝗虫,寓意着它能产生蝗虫般成千上万的并发用户: Locust并不小众,从它Github的Star数量就可见一斑: 截止文章写 ...

  10. 【python】Leetcode每日一题-存在重复元素3

    [python]Leetcode每日一题-存在重复元素3 [题目描述] 给你一个整数数组 nums 和两个整数 k 和 t .请你判断是否存在 两个不同下标 i 和 j,使得 abs(nums[i] ...