Mybatis3詳解(十四)——Mybatis的分頁
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介面。
建立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攔截器方式的一種三方實現,它內部幫助我們實現了Interceptor的功能,因為所有想要物件Mybatis進行分頁的第三方外掛都必須實現Interceptor介面。所以我們在不用自定義MyPageInterceptor時。實際上也是在執行查詢方法之前,進行攔截,然後對分頁引數進行設定。
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); } }
⑥、執行結果