1、前言
在前面學習mybatis的時候,會經常對數據進行增刪改查操作,使用最多的是對數據庫進行查詢操作,但是前面都是簡單的案例,所以查詢的數據量不是很大,自然查詢時沒有任何壓力,但是如果在實際的項目中,數據庫的數據成千上萬,如果還是這樣一次性查詢出所有數據,那么會導致數據可讀性和數據庫性能極差。所以我們往往使用分頁進行查詢,這樣對數據庫壓力就在可控范圍內。
這里介紹Mybatis的這幾種分頁方式:
- 原生SQL的Limit分頁
- Mybatis自帶的RowBounds分頁
- 自定義攔截器插件進行分頁
- 使用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的簡單介紹,還有更多的功能可以去參考官方文檔,也可以自行百度學習。
