4、mybatis(oracle分頁)分頁攔截器

接上一節(jié),增加一個分頁攔截器來實現(xiàn)dao層的分頁邏輯
攔截器就是在不改變源碼的情況下,改變mybatis源碼的一些行為。在sql語句執(zhí)行之前,把普通sql語句換成分頁語句即可。

分析:怎樣攔截
(1)確定攔截對象:
a: 確定什么樣的攔截對象---后綴為Page的方法
b: 攔截對象什么行為---需要分頁操作
c: 什么時候攔截---在mybatis執(zhí)行sql語句之前攔截,準確點是獲取statement之前,在mybatis的源碼中
(2)攔截下來做什么事:代替某個方法完成分頁功能,改進原始的查詢語句
(3)攔截結(jié)束,交回主權(quán)

做這個攔截器之前,復(fù)習(xí)了一下動態(tài)代理。我們的目的是在分頁sql執(zhí)行之前加上我們自己的分頁邏輯,而且多次用到分頁,所以用動態(tài)代理非常合適。此處我們的PageInterceptor 就是自定義的執(zhí)行邏輯,其他工作交給jdk的Proxy,讓它生成代理類,讓代理類執(zhí)行分頁。

例子中mybatis版本3.4.0

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

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.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;


/**
*@Component: 注入到spring boot容器中
 * @Intercepts分頁攔截器,type:要攔截的類的class對象,method:要攔截的方法,args:方法的參數(shù)
 */
@Component
@Intercepts({@Signature(type=StatementHandler.class,method="prepare",args={Connection.class, Integer.class})})
public class PageInterceptor implements Interceptor {
    
    private String test;
    
    private final static Logger logger = LoggerFactory.getLogger(PageInterceptor.class);
    
    // 攔截實現(xiàn)
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler)invocation.getTarget();
        // 對statementHandler進行封裝
        MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, 
                SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());
        MappedStatement mappedStatement = (MappedStatement)metaObject.getValue("delegate.mappedStatement");
        // 配置文件中SQL語句的ID
        String id = mappedStatement.getId();
        if(id.matches(".+Page$")) {
            BoundSql boundSql = statementHandler.getBoundSql();
            // 原始的SQL語句
            String sql = boundSql.getSql();
            logger.info("原始sql:" + sql);
            // 查詢總條數(shù)的SQL語句
            String countSql = "select count(*) from (" + sql + ")a";
            logger.info("查詢總條數(shù)sql:" + countSql);
            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));
            }
            // 改造后帶分頁查詢的SQL語句
//          String pageSql = sql + " limit " + page.getDbIndex() + "," + page.getDbNumber();
            String pageSql = "select * from (select t1.*, rownum rn from (" + sql + 
                    ") t1 ) t2 where rn > " + page.getDbIndex() +" and rn <= " + page.getDbEnd();
            logger.info("分頁sql:" + pageSql);
            metaObject.setValue("delegate.boundSql.sql", pageSql);
        }
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        logger.info(this.test);
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
    }
}

mybatis源碼說明:target被代理對象,Plugin.wrap(target, this)返回的是動態(tài)生成的代理對象

Paste_Image.png

getSignatureMap(interceptor):通過注解獲取要攔截的類,方法等。

Paste_Image.png

先通過setProperties拿到配置文件里的屬性值,再通過plugin方法對攔截的對象進行過濾,最后通過intercept方法執(zhí)行攔截的邏輯。

Paste_Image.png
Paste_Image.png
Paste_Image.png
Paste_Image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1. 簡介 1.1 什么是 MyBatis ? MyBatis 是支持定制化 SQL、存儲過程以及高級映射的優(yōu)秀的...
    笨鳥慢飛閱讀 6,233評論 0 4
  • 1、#{}和${}的區(qū)別是什么?注:這道題是面試官面試我同事的。 答:${}是Properties文件中的變量占位...
    小沙鷹168閱讀 2,272評論 2 64
  • 記錄是一種精神,是加深理解最好的方式之一。 最近看了下Mybatis的源碼,分析了Mybatis插件的實現(xiàn)方式,在...
    曹金桂閱讀 18,285評論 14 51
  • 無聊的日子,本應(yīng)奮斗的青春確荒廢在了大學(xué)的殿堂
    我只想靜靜的一個人閱讀 212評論 0 1

友情鏈接更多精彩內(nèi)容