1. 程式人生 > >Mybatis實現分頁功能

Mybatis實現分頁功能

由於我之前有一篇部落格寫到實現分頁功能,簡單的分頁功能,但是如果我們有多個頁面,就需要每一個頁面都寫一個這個麻煩的程式碼,顯得非常的愚笨。於是出現了Mybatis分頁,有點類似於Spring的AOP(這裡不講解)。

首先,我們是想一個特定查詢的語句執行的時候需要進行攔截,執行分頁的功能。

一般的步驟是寫一個攔截類,然後註冊。

我們需要一個類,這裡叫做PageInterceptor

package com.interceptor;

import main.entity.Page;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
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.ResultSet;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.Map;
import java.util.Properties;

/**
 * @program: Tradingplatform
 * @description:分頁攔截器
 * @author: Robert_Wang
 * @create: 2018-09-16 19:46
 **/
//攔截的類+方法(防止過載,需加上引數)
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class})})
public class PageInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        //獲取攔截目標
        StatementHandler statementHandler = (StatementHandler)invocation.getTarget();
        //元物件, 由於我們想要獲得一個 protected ResultMap,所以用反射來實現
        //將元物件方便我們訪問屬性
        MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY,SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY);
        //按照OGNL表示式 訪問屬性
        //獲取與xml所對應的關鍵資訊
            MappedStatement mappedStatement = (MappedStatement)metaObject.getValue("delegate.mappedStatement");
            //配置檔案中sql的id, 即 queryListByPage
            String id =  mappedStatement.getId();
            //正則表示式 .+ 至少出現一個字元 $是結束符
            if(id.matches(".+ByPage$")){
                BoundSql boundSql = statementHandler.getBoundSql();
                String sql = boundSql.getSql();
                String countSql = "select count(*) from (" + sql + ")a";
                Connection connection = (Connection)invocation.getArgs()[0];
                PreparedStatement countStatement = connection.prepareStatement(countSql);
                ParameterHandler parameterHandler = (ParameterHandler)metaObject.getValue("delegate.parameterHandler");
                parameterHandler.setParameters(countStatement);
                ResultSet rs =  countStatement.executeQuery();
                Map<?,?> parameter = (Map<?,?>)boundSql.getParameterObject();
                Page page = (Page)parameter.get("page");
                if(rs.next()){
                    page.setTotalNumber(rs.getInt(1));
                }
                String pageSql = sql + " limit " + page.getDbIndex() + "," + page.getDbNumber();
                //修改sql語句
                metaObject.setValue("delegate.boundSql.sql",pageSql);

        }
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object o) {
        //返回代理類
        return Plugin.wrap(o,this);
    }

    @Override
    public void setProperties(Properties properties) {

    }
}

plugin方法是返回一個代理過的類,引數是代理的物件。

setProperties是用於設定引數。是在進行註冊的時候設定的引數。

interceptor是攔截的過程,也是本次的核心。

        我們先明確目的:在查詢語句的PreparedStatement前進行攔截,那麼我們就要

        獲得xml中的sql語句, 執行相關的操作(查詢總條數、計算多少個頁面、等)

然後我們再細分這個過程慢慢解決。

攔截的類前面加一個註解,表示攔截該類時簽有型別、方法的類,引數就是區分過載函式。

      首先獲取攔截目標 -->獲取sql語句-->>進行sql相關的操作

再進行細分;

首先獲取攔截目標:StatementHandler

獲取sql語句之前需要在xml獲取,於是需要MappedStatement這個類,這個類是需要元物件MetaObject來獲取的,

而元物件是需要StatementHandler。

於是就關聯起來了,其實用到的就是StatementHandler MetaObject  StatementHandler最主要的三個類。

     然後就是SQL操作了:先判斷攔截的SQL是不是我們想要的SQL,如果是,再查詢條數,設定引數值即可。

再進行細分: 查詢條數:拼接SQL語句-->獲取連線-->預編譯-->引數設定。

其他的細枝末節就這樣分了,下面不再詳細分了。