1. 程式人生 > >Mybatis分頁實現的方法(攔截器+pageHelper)

Mybatis分頁實現的方法(攔截器+pageHelper)

一、攔截器實現

1.原理

在mybatis 執行過程中攔截執行物件,獲得sql資訊,將分頁資訊新增到sql語句中,然後放行mybatis的執行過程

2.瞭解一點mybatis原始碼

首先我們需要明白要攔截的物件是處理物件(Statement),攔截的時機應該是sql執行之前,

所以我們應該攔截的是: @Intercepts({@Signature(type=StatementHandler.class,method = "prepare",args={Connection.class,Integer.class})})

type 表示需要攔截的類, method 表示需要攔截的方法, args 表示引數

我們這裡攔截主要是(其實這裡攔截到的是而是RoutingStatementHandler,它會通過判斷來決定例項化那個StatementHandler, BaseStatementHandler只是這裡Statement的實現類) BaseStatementHandler ,它實現了Statement。 BaseStatementHandler中通過instantiateStatement 方法來獲取statement物件,instantiateStatement的具體實現在PreparedStatementHandler。

上面的敘述可能會有偏差,原始碼的理解有待提高,這裡看原始碼的主要目的是為了清楚sql的具體資訊在哪裡,比如BaseStatementHandler下的mappedStatement對應的就是mapper 中xml的資訊,BoundSql對應一些sql資訊。

為了在程式碼中獲取到sql的具體資訊我們需要藉助MetaObject這個類,它可以使我們不需要寫反射程式碼獲取到一些必要的資訊。

3.具體程式碼編寫過程

首先,將需要分頁的方法名以”ByPage“結尾 或者什麼標誌標記一下,以確保自己能在後面將這些方法過濾出來

然後,編寫攔截器:

page類:

public class Page {

    private int totalNumber;

    private int currentPage;

    private int totalPage;

    private int pageNumber = 5;

    //從第幾條開始
    private int dbIndex;

    //取幾條
    private int dbNumber;

    public void count(){
        int totlaPageTemp = this.totalNumber / this.pageNumber;
        int plus = (this.totalNumber%this.pageNumber)==0?0:1;
        totlaPageTemp += plus;

        if( totlaPageTemp <= 0 ){
            totlaPageTemp = 1;
        }
        this.totalPage = totlaPageTemp;

        if( this.totalPage < this.currentPage ){
            this.currentPage = this.totalPage;
        }

        if( this.currentPage < 1 ){
            this.currentPage = 1;
        }

        this.dbIndex = (this.currentPage-1)*this.pageNumber;
        this.dbNumber = this.pageNumber;
    }

    public int getTotalNumber() {
        return totalNumber;
    }

    public void setTotalNumber(int totalNumber) {
        this.totalNumber = totalNumber;
    }

    public int getCurrentPage() {
        return currentPage;
    }

    public void setCurrentPage(int currentPage) {
        this.currentPage = currentPage;
    }

    public int getTotalPage() {
        return totalPage;
    }

    public void setTotalPage(int totalPage) {
        this.totalPage = totalPage;
    }

    public int getPageNumber() {
        return pageNumber;
    }

    public void setPageNumber(int pageNumber) {
        this.pageNumber = pageNumber;
    }

    public int getDbIndex() {
        return dbIndex;
    }

    public void setDbIndex(int dbIndex) {
        this.dbIndex = dbIndex;
    }

    public int getDbNumber() {
        return dbNumber;
    }

    public void setDbNumber(int dbNumber) {
        this.dbNumber = dbNumber;
    }

    @Override
    public String toString() {
        return "Page{" +
                "totalNumber=" + totalNumber +
                ", currentPage=" + currentPage +
                ", totalPage=" + totalPage +
                ", pageNumber=" + pageNumber +
                ", dbIndex=" + dbIndex +
                ", dbNumber=" + dbNumber +
                '}';
    }
}
/**
 * mybatis 分頁攔截器
 */
@Intercepts({@Signature(type=StatementHandler.class,method = "prepare",args={Connection.class,Integer.class})})
public class PageInterceptor implements Interceptor {
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        MetaObject metaObject = MetaObject.forObject(statementHandler,SystemMetaObject.DEFAULT_OBJECT_FACTORY,SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY,new DefaultReflectorFactory());
        /*
        從RoutingStatementHandler中獲得處理物件PreparedStatementHandler,從這個物件中獲取Mapper中的xml資訊
        * */
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        //配置檔案中sql語句的id
        String id = mappedStatement.getId();
        //判斷是否是需要分頁的方法
        if( id.matches(".+ByPage$") ){
            //獲得sql的引數資訊
            BoundSql boundSql = statementHandler.getBoundSql();
            Map<String, Object> parameter = (Map<String, Object>)boundSql.getParameterObject();
            Page page = (Page)parameter.get("page");
            //原始的sql
            String sql = boundSql.getSql();
            System.out.println("未改造的sql語句:"+sql);
            //組裝page中的資訊,查詢總條數
            String countSql = "select count(*) from ("+sql+")a";
            System.out.println("查詢總條數sql語句:"+countSql);
            Connection connection = (Connection)invocation.getArgs()[0];
            PreparedStatement statement = connection.prepareStatement(countSql);
            ParameterHandler parameterHandler = (ParameterHandler)metaObject.getValue("delegate.parameterHandler");
            parameterHandler.setParameters(statement);
            ResultSet rs = statement.executeQuery();
            if( rs.next() ){
                int total = rs.getInt(1);
                page.setTotalNumber(total);
                System.out.println("總條數:"+total);
            }else{
                System.out.println("未查詢到資料量");
            }
            page.count();

            //改造後的sql語句
            String pageSql = sql + " limit "+ page.getDbIndex()+" , "+page.getDbNumber();
            System.out.println("改造後的sql語句:"+pageSql);
            metaObject.setValue("delegate.boundSql.sql",pageSql);
        }
        //放行
        return invocation.proceed();
    }

    //被攔截的請求
    public Object plugin(Object o) {
        return Plugin.wrap(o,this);
    }

    public void setProperties(Properties properties) {
        //這裡可以拿到配置檔案中 plugin 中的propertie的值
    }
}

最後,在配置檔案中註冊攔截器

    <plugins>
        <plugin interceptor="per.ly.interceptor.PageInterceptor" />
    </plugins>

4.總結

使用不夠快捷。

二、pageHelper

1、新增依賴

    <dependency>
      <groupId>com.github.pagehelper</groupId>
      <artifactId>pagehelper</artifactId>
      <version>5.1.2</version>
    </dependency>

2、在mybatis配置檔案中配置

    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor">
        </plugin>
    </plugins>

3、實現簡單分頁

PageHelper.startPage(pagenum,pagesize);

4、總結

只能對查詢出的結果進行分頁,可能無法滿足要求。