1. 程式人生 > >MyBatis精通之路之分頁功能的實現

MyBatis精通之路之分頁功能的實現

avi 一個 冗余 details 結果 nature ann email ret

MyBatis精通之路之分頁功能的實現(數組分頁、sql分頁、攔截器,RowBounds分頁)

原創 2017年04月27日 21:34:48

前言:學習hibernate & mybatis等持久層框架的時候,不外乎對數據庫的增刪改查操作。而使用最多的當是數據庫的查找操作, 而當數據庫數據過多時,符合查找條件的數據可能也會是很龐大的數據。往往在這個時候,我們都不會希望一次性的將所有的數據一起性讀取出來,並且顯示在UI界面上。常用的操作,就是對查找到的數據進行分頁,每次處理小部分數據。這樣每次處理的數據量就會在可控的範圍,UI的展示也會很協調。

問題:面對上面的問題,今天我們就來進行基於mybatis和MySql進行分頁功能的實現。常見的數據分頁有哪幾種實現??基於數組的分頁實現?基於sql語句的分頁實現?還是通過攔截器進行數據分頁功能?還是通過RowBounds參數進行物理分頁?幾種都是常用的分頁實現原理,接下來就按照數組、sql語句,攔截器和RowBounds的方式介紹分頁功能。

一.借助數組進行分頁

原理:進行數據庫查詢操作時,獲取到數據庫中所有滿足條件的記錄,保存在應用的臨時數組中,再通過List的subList方法,獲取到滿足條件的所有記錄。

實現:

首先在dao層,創建StudentMapper接口,用於對數據庫的操作。在接口中定義通過數組分頁的查詢方法,如下所示:

 List<Student> queryStudentsByArray();
  • 1

方法很簡單,就是獲取所有的數據,通過list接收後進行分頁操作。

創建StudentMapper.xml文件,編寫查詢的sql語句:

 <select id="queryStudentsByArray"  resultMap="studentmapper">
        select * from student
 </select>
  • 1
  • 2
  • 3

可以看出再編寫sql語句的時候,我們並沒有作任何分頁的相關操作。這裏是查詢到所有的學生信息。

接下來在service層獲取數據並且進行分頁實現:

定義IStuService接口,並且定義分頁方法:

List<Student> queryStudentsByArray(int currPage, int pageSize);
  • 1

通過接收currPage參數表示顯示第幾頁的數據,pageSize表示每頁顯示的數據條數。

創建IStuService接口實現類StuServiceIml對方法進行實現,對獲取到的數組通過currPage和pageSize進行分頁:

 @Override
    public List<Student> queryStudentsByArray(int currPage, int pageSize) {
        List<Student> students = studentMapper.queryStudentsByArray();
//        從第幾條數據開始
        int firstIndex = (currPage - 1) * pageSize;
//        到第幾條數據結束
        int lastIndex = currPage * pageSize;
        return students.subList(firstIndex, lastIndex);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

通過subList方法,獲取到兩個索引間的所有數據。

最後在controller中創建測試方法:

  @ResponseBody
    @RequestMapping("/student/array/{currPage}/{pageSize}")
    public List<Student> getStudentByArray(@PathVariable("currPage") int currPage, @PathVariable("pageSize") int pageSize) {
        List<Student> student = StuServiceIml.queryStudentsByArray(currPage, pageSize);
        return student;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

通過用戶傳入的currPage和pageSize獲取指定數據。

測試:

首先我們來獲取再沒實現分頁效果前獲取到的所有數據,如下所示:
技術分享圖片

接下來在瀏覽器輸入http://localhost:8080/student/student/array/1/2測試實現了分頁後的數據。獲取第一頁的數據,每頁顯示兩條數據。

結果如下:
技術分享圖片
輸出的是指定的從第0-2條數據,可見我們通過數組分頁的功能是成功的。(這裏因為用到了關聯查詢,所以看起來數據可能比較多)

缺點:數據庫查詢並返回所有的數據,而我們需要的只是極少數符合要求的數據。當數據量少時,還可以接受。當數據庫數據量過大時,每次查詢對數據庫和程序的性能都會產生極大的影響。

二.借助Sql語句進行分頁

在了解到通過數組分頁的缺陷後,我們發現不能每次都對數據庫中的所有數據都檢索。然後在程序中對獲取到的大量數據進行二次操作,這樣對空間和性能都是極大的損耗。所以我們希望能直接在數據庫語言中只檢索符合條件的記錄,不需要在通過程序對其作處理。這時,Sql語句分頁技術橫空出世。

實現:通過sql語句實現分頁也是非常簡單的,只是需要改變我們查詢的語句就能實現了,即在sql語句後面添加limit分頁語句。

首先還是在StudentMapper接口中添加sql語句查詢的方法,如下:

List<Student> queryStudentsBySql(Map<String,Object> data);
  • 1

然後在StudentMapper.xml文件中編寫sql語句通過limiy關鍵字進行分頁:

 <select id="queryStudentsBySql" parameterType="map" resultMap="studentmapper">
        select * from student limit #{currIndex} , #{pageSize}
</select>
  • 1
  • 2
  • 3

接下來還是在IStuService接口中定義方法,並且在StuServiceIml中對sql分頁實現。

List<Student> queryStudentsBySql(int currPage, int pageSize);
  • 1
 @Override
    public List<Student> queryStudentsBySql(int currPage, int pageSize) {
        Map<String, Object> data = new HashedMap();
        data.put("currIndex", (currPage-1)*pageSize);
        data.put("pageSize", pageSize);
        return studentMapper.queryStudentsBySql(data);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

sql分頁語句如下:select * from table limit index, pageSize;

所以在service中計算出currIndex:要開始查詢的第一條記錄的索引。

測試:

在瀏覽器輸入http://localhost:8080/student/student/sql/1/2獲取第一頁的數據,每頁顯示兩條數據。

結果:
技術分享圖片
從輸出結果可以看出和數組分頁的結果是一致的,因此sql語句的分頁也是沒問題的。

缺點:雖然這裏實現了按需查找,每次檢索得到的是指定的數據。但是每次在分頁的時候都需要去編寫limit語句,很冗余。而且不方便統一管理,維護性較差。所以我們希望能夠有一種更方便的分頁實現。

三.攔截器分頁

上面提到的數組分頁和sql語句分頁都不是我們今天講解的重點,今天需要實現的是利用攔截器達到分頁的效果。自定義攔截器實現了攔截所有以ByPage結尾的查詢語句,並且利用獲取到的分頁相關參數統一在sql語句後面加上limit分頁的相關語句,一勞永逸。不再需要在每個語句中單獨去配置分頁相關的參數了。。

首先我們看一下攔截器的具體實現,在這裏我們需要攔截所有以ByPage結尾的所有查詢語句,因此要使用該攔截器實現分頁功能,那麽再定義名稱的時候需要滿足它攔截的規則(以ByPage結尾),如下所示:

package com.cbg.interceptor;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;

import java.sql.Connection;
import java.util.Map;
import java.util.Properties;

/**
 * Created by chenboge on 2017/5/7.
 * <p>
 * Email:[email protected]
 * <p>
 * description:
 */

/**
 * @Intercepts 說明是一個攔截器
 * @Signature 攔截器的簽名
 * type 攔截的類型 四大對象之一( Executor,ResultSetHandler,ParameterHandler,StatementHandler)
 * method 攔截的方法
 * args 參數
 */
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class MyPageInterceptor implements Interceptor {

//每頁顯示的條目數
    private int pageSize;
//當前現實的頁數
    private int currPage;

    private String dbType;


    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        //獲取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");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 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
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118

上面即是攔截器功能的實現,在intercept方法中獲取到select標簽和sql語句的相關信息,攔截所有以ByPage結尾的select查詢,並且統一在查詢語句後面添加limit分頁的相關語句,統一實現分頁功能。

重點詳解:

StatementHandler是一個接口,而我們在代碼中通過StatementHandler statementHandler = (StatementHandler) invocation.getTarget();獲取到的是StatementHandler默認的實現類RoutingStatementHandler。而RoutingStatementHandler只是一個中間代理,他不會提供具體的方法。那你可能會納悶了,攔截器中基本上是依賴statementHandler獲取各種對象和屬性的,沒有具體屬性和方法怎麽行??接著看下面代碼:

private final StatementHandler delegate;

    public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        switch(RoutingStatementHandler.SyntheticClass_1.$SwitchMap$org$apache$ibatis$mapping$StatementType[ms.getStatementType().ordinal()]) {
        case 1:
            this.delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
        case 2:
            this.delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
        case 3:
            this.delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
        default:
            throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
        }

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

原來它是通過不同的MappedStatement創建不同的StatementHandler實現類對象處理不同的情況。這裏的到的StatementHandler實現類才是真正服務的。看到這裏,你可能就會明白MappedStatement mappedStatement = (MappedStatement) MetaObjectHandler.getValue("delegate.mappedStatement");中delegate的來源了吧。至於為什麽要這麽去獲取,後面我們會說道。

拿到statementHandler後,我們會通過MetaObject MetaObjectHandler = SystemMetaObject.forObject(statementHandler);去獲取它的包裝對象,通過包裝對象去獲取各種服務。

MetaObject:mybatis的一個工具類,方便我們有效的讀取或修改一些重要對象的屬性。四大對象(ResultSetHandler,ParameterHandler,Executor和statementHandler)提供的公共方法很少,要想直接獲取裏面屬性的值很困難,但是可以通過MetaObject利用一些技術(內部反射實現)很輕松的讀取或修改裏面的數據。

接下來說說:MappedStatement mappedStatement = (MappedStatement) MetaObjectHandler.getValue("delegate.mappedStatement");

上面提到為什麽要這麽去獲取MappedStatement對象??在RoutingStatementHandler中delegate是私有的(private final StatementHandler delegate;),有沒有共有的方法去獲取。所以這裏只有通過反射來獲取啦。

MappedStatement是保存了xxMapper.xml中一個sql語句節點的所有信息的包裝類,可以通過它獲取到節點中的所有信息。在示例中我們拿到了id值,也就是方法的名稱,通過名稱區攔截所有需要分頁的請求。

通過StatementHandler的包裝類,不光能拿到MappedStatement,還可以拿到下面的數據:

    public abstract class BaseStatementHandler implements StatementHandler {
    protected final Configuration configuration;
    protected final ObjectFactory objectFactory;
    protected final TypeHandlerRegistry typeHandlerRegistry;
    protected final ResultSetHandler resultSetHandler;
    protected final ParameterHandler parameterHandler;
    protected final Executor executor;
    protected final MappedStatement mappedStatement;
    protected final RowBounds rowBounds;
    protected BoundSql boundSql;

    protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        this.configuration = mappedStatement.getConfiguration();
        this.executor = executor;
        this.mappedStatement = mappedStatement;
        this.rowBounds = rowBounds;
        this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
        this.objectFactory = this.configuration.getObjectFactory();
        if(boundSql == null) {
            this.generateKeys(parameterObject);
            boundSql = mappedStatement.getBoundSql(parameterObject);
        }

        this.boundSql = boundSql;
        this.parameterHandler = this.configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
        this.resultSetHandler = this.configuration.newResultSetHandler(executor, mappedStatement, rowBounds, this.parameterHandler, resultHandler, boundSql);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

上面的所有數據都可以通過反射拿到。

幾個重要的參數:
Configuration:所有配置的相關信息。
ResultSetHandler:用於攔截執行結果的組裝。
ParameterHandler:攔截執行Sql的參數的組裝。
Executor:執行Sql的全過程,包括組裝參數、組裝結果和執行Sql的過程。
BoundSql:執行的Sql的相關信息。

接下來我們通過如下代碼拿到請求時的map對象(反射)。

 //獲取進行數據庫操作時管理參數的handler
            ParameterHandler parameterHandler = (ParameterHandler) MetaObjectHandler.getValue("delegate.parameterHandler");
            //獲取請求時的參數
            Map<String, Object> paraObject = (Map<String, Object>) parameterHandler.getParameterObject();
            //也可以這樣獲取
            //paraObject = (Map<String, Object>) statementHandler.getBoundSql().getParameterObject();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

拿到我們需要的currPage和pageSize參數後,就是組裝分頁查詢的sql語句’limitSql‘了。

最後通過MetaObjectHandler.setValue("delegate.boundSql.sql", limitSql);將原始的sql語句替換成我們新的分頁語句,完成偷天換日的功能,接下來讓代碼繼續執行。

編寫好攔截器後,需要註冊到項目中,才能發揮它的作用。在mybatis的配置文件中,添加如下代碼:

    <plugins>
        <plugin interceptor="com.cbg.interceptor.MyPageInterceptor">
            <property name="limit" value="10"/>
            <property name="dbType" value="mysql"/>
        </plugin>
    </plugins>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

如上所示,還能在裏面配置一些屬性,在攔截器的setProperties方法中可以獲取配置好的屬性值。如項目分頁的pageSize參數的值固定,我們就可以配置在這裏了,以後就不需要每次傳入pageSize了,讀取方式如下:

 //讀取配置的代理對象的參數
    @Override
    public void setProperties(Properties properties) {
        String limit1 = properties.getProperty("limit", "10");
        this.pageSize = Integer.valueOf(limit1);
        this.dbType = properties.getProperty("dbType", "mysql");
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

到這裏,有關攔截器的相關知識就講解的差不多了,接下來就需要測試,是否我們這樣寫真的有效??

首先還是添加dao層的方法和xml文件的sql語句配置,註意項目中攔截的是以ByPage結尾的請求,所以在這裏,我們的方法名稱也以此結尾:

方法
List<Student> queryStudentsByPage(Map<String,Object> data);

xml文件的select語句
    <select id="queryStudentsByPage" parameterType="map" resultMap="studentmapper">
        select * from student
    </select>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

可以看出,這裏我們就不需要再去手動配置分頁語句了。

接下來是service層的接口編寫和實現方法:

方法:
List<Student> queryStudentsByPage(int currPage,int pageSize);

實現:
 @Override
    public List<Student> queryStudentsByPage(int currPage, int pageSize) {
        Map<String, Object> data = new HashedMap();
        data.put("currPage", currPage);
        data.put("pageSize", pageSize);
        return studentMapper.queryStudentsByPage(data);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

這裏我們雖然傳入了currPage和pageSize兩個參數,但是在sql的xml文件中並沒有使用,直接在攔截器中獲取到統一使用。

最後編寫controller的測試代碼:

 @ResponseBody
    @RequestMapping("/student/page/{currPage}/{pageSize}")
    public List<Student> getStudentByPage(@PathVariable("currPage") int currPage, @PathVariable("pageSize") int pageSize) {
        List<Student> student = StuServiceIml.queryStudentsByPage(currPage, pageSize);
        return student;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

測試:
在瀏覽器輸入:http://localhost:8080/student/student/page/1/2

結果:
技術分享圖片
可見和上面兩種分頁的效果是一樣的。

四.RowBounds實現分頁

原理:通過RowBounds實現分頁和通過數組方式分頁原理差不多,都是一次獲取所有符合條件的數據,然後在內存中對大數據進行操作,實現分頁效果。只是數組分頁需要我們自己去實現分頁邏輯,這裏更加簡化而已。

存在問題:一次性從數據庫獲取的數據可能會很多,對內存的消耗很大,可能導師性能變差,甚至引發內存溢出。

適用場景:在數據量很大的情況下,建議還是適用攔截器實現分頁效果。RowBounds建議在數據量相對較小的情況下使用。

簡單介紹:這是代碼實現上最簡單的一種分頁方式,只需要在dao層接口中要實現分頁的方法中加入RowBounds參數,然後在service層通過offset(從第幾行開始讀取數據,默認值為0)和limit(要顯示的記錄條數,默認為java允許的最大整數:2147483647)兩個參數構建出RowBounds對象,在調用dao層方法的時,將構造好的RowBounds傳進去就能輕松實現分頁效果了。

具體操作如下:

dao層接口方法:

//加入RowBounds參數
public List<UserBean> queryUsersByPage(String userName, RowBounds rowBounds);
  • 1
  • 2

然後在service層構建RowBounds,調用dao層方法:

  @Override
    @Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.SUPPORTS)
    public List<RoleBean> queryRolesByPage(String roleName, int start, int limit) {
        return roleDao.queryRolesByPage(roleName, new RowBounds(start, limit));
    }
  • 1
  • 2
  • 3
  • 4
  • 5

RowBounds就是一個封裝了offset和limit簡單類,如下所示:

public class RowBounds {
    public static final int NO_ROW_OFFSET = 0;
    public static final int NO_ROW_LIMIT = 2147483647;
    public static final RowBounds DEFAULT = new RowBounds();
    private int offset;
    private int limit;

    public RowBounds() {
        this.offset = 0;
        this.limit = 2147483647;
    }

    public RowBounds(int offset, int limit) {
        this.offset = offset;
        this.limit = limit;
    }

    public int getOffset() {
        return this.offset;
    }

    public int getLimit() {
        return this.limit;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

只需要這兩步操作,就能輕松實現分頁效果了,是不是很神奇。但卻不簡單,內部是怎麽實現的??給大家提供一個簡單的思路:RowBounds分頁簡單原理

結論:從上面四種sql分頁的實現方式可以看出,通過RowBounds實現是最簡便的,但是通過攔截器的實現方式是最優的方案。只需一次編寫,所有的分頁方法共同使用,還可以避免多次配置時的出錯機率,需要修改時也只需要修改這一個文件,一勞永逸。而且是我們自己實現的,便於我們去控制和增加一些邏輯處理,使我們在外層更簡單的使用。同時也不會出現數組分頁和RowBounds分頁導致的性能問題。當然,具體情況可以采取不同的解決方案。數據量小時,RowBounds不失為一種好辦法。但是數據量大時,實現攔截器就很有必要了。

到這裏,mybatis的分頁原理和全部實現過程都完成了,還有不清楚的可以自己去看一下mybatis的源碼,按照這個思路去閱讀還是比較清晰的。這裏只是對插件(攔截器)實現分頁做了個簡單的介紹,只是簡單的分頁功能,還很簡陋。在下一遍博客我們將會實現一個封裝好的、功能齊全的實用性插件。傳送門:mybatis精通之路之插件分頁(攔截器)進階
最後,希望大家提出寶貴意見,共同學習。

版權聲明:本文為博主原創文章,未經博主允許不得轉載。

MyBatis精通之路之分頁功能的實現