最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

解決mybatis分頁(yè)插件PageHelper導(dǎo)致自定義攔截器失效

 更新時(shí)間:2022年08月17日 15:46:39   作者:吃葡萄不吐葡糖皮  
這篇文章主要為大家介紹了解決mybatis分頁(yè)插件PageHelper導(dǎo)致自定義攔截器失效方案示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

問(wèn)題背景

在最近的項(xiàng)目開(kāi)發(fā)中遇到一個(gè)需求 需要對(duì)mysql做一些慢查詢(xún)、大結(jié)果集等異常指標(biāo)進(jìn)行收集監(jiān)控,從運(yùn)維角度并沒(méi)有對(duì)mysql進(jìn)行統(tǒng)一的指標(biāo)搜集,所以需要通過(guò)代碼層面對(duì)指標(biāo)進(jìn)行收集,我采用的方法是通過(guò)mybatis的Interceptor攔截器進(jìn)行指標(biāo)收集在開(kāi)發(fā)中出現(xiàn)了自定義攔截器 對(duì)于查詢(xún)無(wú)法進(jìn)行攔截的問(wèn)題幾經(jīng)周折后終于解決,故進(jìn)行記錄學(xué)習(xí),分享給大家下次遇到少走一些彎路;

mybatis攔截器使用

像springmvc一樣,mybatis也提供了攔截器實(shí)現(xiàn),對(duì)Executor、StatementHandler、ResultSetHandler、ParameterHandler提供了攔截器功能。

使用方法:

在使用時(shí)我們只需要 implements org.apache.ibatis.plugin.Interceptor類(lèi)實(shí)現(xiàn) 方法頭標(biāo)注相應(yīng)注解即可 如下代碼會(huì)對(duì)CRUD的操作進(jìn)行攔截:

@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})

注解參數(shù)介紹:

  • @Intercepts:標(biāo)識(shí)該類(lèi)是一個(gè)攔截器;
  • @Signature:指明自定義攔截器需要攔截哪一個(gè)類(lèi)型,哪一個(gè)方法
攔截的類(lèi)(type)攔截的方法(method)
Executorupdate, query, flushStatements, commit, rollback,getTransaction, close, isClosed
ParameterHandlergetParameterObject, setParameters
StatementHandlerprepare, parameterize, batch, update, query
ResultSetHandlerhandleResultSets, handleOutputParameters
  • Executor:提供了增刪改查的接口 攔截執(zhí)行器的方法.
  • StatementHandler:負(fù)責(zé)處理Mybatis與JDBC之間Statement的交互 攔截參數(shù)的處理.
  • ResultSetHandler:負(fù)責(zé)處理Statement執(zhí)行后產(chǎn)生的結(jié)果集,生成結(jié)果列表 攔截結(jié)果集的處理.
  • ParameterHandler:是Mybatis實(shí)現(xiàn)Sql入?yún)⒃O(shè)置的對(duì)象 攔截Sql語(yǔ)法構(gòu)建的處理。

官方代碼示例:

@Intercepts({@Signature(type = Executor.class, method = "query",
        args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class TestInterceptor implements Interceptor {
   public Object intercept(Invocation invocation) throws Throwable {
     Object target = invocation.getTarget(); //被代理對(duì)象
     Method method = invocation.getMethod(); //代理方法
     Object[] args = invocation.getArgs(); //方法參數(shù)
     // do something ...... 方法攔截前執(zhí)行代碼塊
     Object result = invocation.proceed();
     // do something .......方法攔截后執(zhí)行代碼塊
     return result;
   }
   public Object plugin(Object target) {
     return Plugin.wrap(target, this);
   }
}

setProperties方法

因?yàn)閙ybatis框架本身就是一個(gè)可以獨(dú)立使用的框架,沒(méi)有像Spring這種做了很多的依賴(lài)注入。 如果我們的攔截器需要一些變量對(duì)象,而且這個(gè)對(duì)象是支持可配置的。

類(lèi)似于Spring中的@Value("${}")從application.properties文件中獲取。

使用方法:

mybatis-config.xml配置:

<plugin interceptor="com.plugin.mybatis.MyInterceptor">
     <property name="username" value="xxx"/>
     <property name="password" value="xxx"/>
</plugin>

方法中獲取參數(shù):properties.getProperty("username");

bug內(nèi)容:

update類(lèi)型操作可以正常攔截 query類(lèi)型查詢(xún)sql無(wú)法進(jìn)入自定義攔截器,導(dǎo)致攔截失敗以下為部分源碼 由于涉及到公司代碼以下代碼做了mask的處理

自定義攔截器部分代碼

@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class SQLInterceptor implements Interceptor { 
@Override
public Object intercept(Invocation invocation) throws Throwable {
.....
}
@Override
public Object plugin(Object target) {
    return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
.....
}
}

自定義攔截器攔截的是Executor執(zhí)行器4參數(shù)query方法和update類(lèi)型方法 由于mybatis的攔截器為責(zé)任鏈模式調(diào)用有一個(gè)傳遞機(jī)制 (第一個(gè)攔截器執(zhí)行完向下一個(gè)攔截器傳遞 具體實(shí)現(xiàn)可以看一下源碼)

update的操作執(zhí)行確實(shí)進(jìn)了自定義攔截器但是查詢(xún)的操作始終進(jìn)不來(lái)后通過(guò)追蹤源碼發(fā)現(xiàn)

pagehelper插件的 PageInterceptor 攔截器 會(huì)對(duì) Executor執(zhí)行器method=query 的4參數(shù)方法進(jìn)行修改轉(zhuǎn)化為 6參數(shù)方法 向下傳遞 導(dǎo)致執(zhí)行順序在pagehelper后面的攔截器的Executor執(zhí)行器4參數(shù)query方法不會(huì)接收到傳遞過(guò)來(lái)的請(qǐng)求導(dǎo)致攔截器失效

PageInterceptor源碼:

/**
 * Mybatis - 通用分頁(yè)攔截器
 * <p>
 * GitHub: https://github.com/pagehelper/Mybatis-PageHelper
 * <p>
 * Gitee : https://gitee.com/free/Mybatis_PageHelper
 *
 * @author liuzh/abel533/isea533
 * @version 5.0.0
 */
@SuppressWarnings({"rawtypes", "unchecked"})
@Intercepts(
        {
                @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
                @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
        }
)
public class PageInterceptor implements Interceptor {
    private volatile Dialect dialect;
    private String countSuffix = "_COUNT";
    protected Cache<String, MappedStatement> msCountMap = null;
    private String default_dialect_class = "com.github.pagehelper.PageHelper";
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        try {
            Object[] args = invocation.getArgs();
            MappedStatement ms = (MappedStatement) args[0];
            Object parameter = args[1];
            RowBounds rowBounds = (RowBounds) args[2];
            ResultHandler resultHandler = (ResultHandler) args[3];
            Executor executor = (Executor) invocation.getTarget();
            CacheKey cacheKey;
            BoundSql boundSql;
            //由于邏輯關(guān)系,只會(huì)進(jìn)入一次
            if (args.length == 4) {
                //4 個(gè)參數(shù)時(shí)
                boundSql = ms.getBoundSql(parameter);
                cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);
            } else {
                //6 個(gè)參數(shù)時(shí)
                cacheKey = (CacheKey) args[4];
                boundSql = (BoundSql) args[5];
            }
            checkDialectExists();
            List resultList;
            //調(diào)用方法判斷是否需要進(jìn)行分頁(yè),如果不需要,直接返回結(jié)果
            if (!dialect.skip(ms, parameter, rowBounds)) {
                //判斷是否需要進(jìn)行 count 查詢(xún)
                if (dialect.beforeCount(ms, parameter, rowBounds)) {
                    //查詢(xún)總數(shù)
                    Long count = count(executor, ms, parameter, rowBounds, resultHandler, boundSql);
                    //處理查詢(xún)總數(shù),返回 true 時(shí)繼續(xù)分頁(yè)查詢(xún),false 時(shí)直接返回
                    if (!dialect.afterCount(count, parameter, rowBounds)) {
                        //當(dāng)查詢(xún)總數(shù)為 0 時(shí),直接返回空的結(jié)果
                        return dialect.afterPage(new ArrayList(), parameter, rowBounds);
                    }
                }
                resultList = ExecutorUtil.pageQuery(dialect, executor,
                        ms, parameter, rowBounds, resultHandler, boundSql, cacheKey);
            } else {
                //rowBounds用參數(shù)值,不使用分頁(yè)插件處理時(shí),仍然支持默認(rèn)的內(nèi)存分頁(yè)
                resultList = executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
            }
            return dialect.afterPage(resultList, parameter, rowBounds);
        } finally {
            if(dialect != null){
                dialect.afterAll();
            }
        }
    }
    /**
     * Spring bean 方式配置時(shí),如果沒(méi)有配置屬性就不會(huì)執(zhí)行下面的 setProperties 方法,就不會(huì)初始化
     * <p>
     * 因此這里會(huì)出現(xiàn) null 的情況 fixed #26
     */
    private void checkDialectExists() {
        if (dialect == null) {
            synchronized (default_dialect_class) {
                if (dialect == null) {
                    setProperties(new Properties());
                }
            }
        }
    }
    private Long count(Executor executor, MappedStatement ms, Object parameter,
                       RowBounds rowBounds, ResultHandler resultHandler,
                       BoundSql boundSql) throws SQLException {
        String countMsId = ms.getId() + countSuffix;
        Long count;
        //先判斷是否存在手寫(xiě)的 count 查詢(xún)
        MappedStatement countMs = ExecutorUtil.getExistedMappedStatement(ms.getConfiguration(), countMsId);
        if (countMs != null) {
            count = ExecutorUtil.executeManualCount(executor, countMs, parameter, boundSql, resultHandler);
        } else {
            countMs = msCountMap.get(countMsId);
            //自動(dòng)創(chuàng)建
            if (countMs == null) {
                //根據(jù)當(dāng)前的 ms 創(chuàng)建一個(gè)返回值為 Long 類(lèi)型的 ms
                countMs = MSUtils.newCountMappedStatement(ms, countMsId);
                msCountMap.put(countMsId, countMs);
            }
            count = ExecutorUtil.executeAutoCount(dialect, executor, countMs, parameter, boundSql, rowBounds, resultHandler);
        }
        return count;
    }
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }
    @Override
    public void setProperties(Properties properties) {
        //緩存 count ms
        msCountMap = CacheFactory.createCache(properties.getProperty("msCountCache"), "ms", properties);
        String dialectClass = properties.getProperty("dialect");
        if (StringUtil.isEmpty(dialectClass)) {
            dialectClass = default_dialect_class;
        }
        try {
            Class<?> aClass = Class.forName(dialectClass);
            dialect = (Dialect) aClass.newInstance();
        } catch (Exception e) {
            throw new PageException(e);
        }
        dialect.setProperties(properties);
        String countSuffix = properties.getProperty("countSuffix");
        if (StringUtil.isNotEmpty(countSuffix)) {
            this.countSuffix = countSuffix;
        }
    }
}
}

解決方法:

通過(guò)上述我們定位到了問(wèn)題產(chǎn)生的原因 解決起來(lái)就簡(jiǎn)單多了 有倆個(gè)方案如下:

  • 調(diào)整攔截器順序 讓自定義攔截器先執(zhí)行
  • 自定義攔截器query方法也定義為 6參數(shù)方法或者不使用Executor.class執(zhí)行器使用StatementHandler.class執(zhí)行器也可以實(shí)現(xiàn)攔截

解決方案一 調(diào)整執(zhí)行順序

mybatis-config.xml 代碼

我們的自定義攔截器配置的執(zhí)行順序是在PageInterceptor這個(gè)攔截器前面的(先配置后執(zhí)行)

<plugins>
    <!-- com.github.pagehelper為PageHelper類(lèi)所在包名 -->
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
        <!-- 使用下面的方式配置參數(shù),后面會(huì)有所有的參數(shù)介紹 -->
        <!-- reasonable:分頁(yè)合理化參數(shù),默認(rèn)值為false。當(dāng)該參數(shù)設(shè)置為 true 時(shí),pageNum<=0 時(shí)會(huì)查詢(xún)第一頁(yè), pageNum>pages(超過(guò)總數(shù)時(shí)),會(huì)查詢(xún)最后一頁(yè)。默認(rèn)false 時(shí),直接根據(jù)參數(shù)進(jìn)行查詢(xún)。-->
        <property name="reasonable" value="true"/>
        <!-- supportMethodsArguments:支持通過(guò) Mapper 接口參數(shù)來(lái)傳遞分頁(yè)參數(shù),默認(rèn)值false,分頁(yè)插件會(huì)從查詢(xún)方法的參數(shù)值中,自動(dòng)根據(jù)上面 params 配置的字段中取值,查找到合適的值時(shí)就會(huì)自動(dòng)分頁(yè)。 使用方法可以參考測(cè)試代碼中的 com.github.pagehelper.test.basic 包下的 ArgumentsMapTest 和 ArgumentsObjTest。-->
        <property name="supportMethodsArguments" value="true"/>
        <!-- autoRuntimeDialect:默認(rèn)值為 false。設(shè)置為 true 時(shí),允許在運(yùn)行時(shí)根據(jù)多數(shù)據(jù)源自動(dòng)識(shí)別對(duì)應(yīng)方言的分頁(yè) (不支持自動(dòng)選擇sqlserver2012,只能使用sqlserver),用法和注意事項(xiàng)參考下面的場(chǎng)景五-->
        <property name="autoRuntimeDialect" value="true"/>
        <!-- params:為了支持startPage(Object params)方法,增加了該參數(shù)來(lái)配置參數(shù)映射,用于從對(duì)象中根據(jù)屬性名取值, 可以配置 pageNum,pageSize,count,pageSizeZero,reasonable,不配置映射的用默認(rèn)值, 默認(rèn)值為pageNum=pageNum;pageSize=pageSize;count=countSql;reasonable=reasonable;pageSizeZero=pageSizeZero。-->
    </plugin>
    <plugin interceptor="com.a.b.common.sql.SQLInterceptor"/>
</plugins>

注意點(diǎn)?。?!

pageHelper的依賴(lài)jar一定要使用pageHelper原生的jar包 pagehelper-spring-boot-starter jar包 是和spring集成的 PageInterceptor會(huì)由spring進(jìn)行管理 在mybatis加載完后就加載了PageInterceptor 會(huì)導(dǎo)致mybatis-config.xml 里調(diào)整攔截器順序失效

錯(cuò)誤依賴(lài):

<dependency>-->
    <!--<groupId>com.github.pagehelper</groupId>-->
    <!--<artifactId>pagehelper-spring-boot-starter</artifactId>-->
    <!--<version>1.2.12</version>-->
</dependency>

正確依賴(lài)

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

解決方案二 修改攔截器注解定義

@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class})
})

或者

@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
        @Signature(type = StatementHandler.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class,ResultHandler.class})
})

以上就是解決mybatis分頁(yè)插件PageHelper導(dǎo)致自定義攔截器失效的詳細(xì)內(nèi)容,更多關(guān)于mybatis PageHelper攔截器失效的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 如何在yml配置文件中使用中文注解

    如何在yml配置文件中使用中文注解

    這篇文章主要介紹了如何在yml配置文件中使用中文注解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • MyBatis基本實(shí)現(xiàn)過(guò)程

    MyBatis基本實(shí)現(xiàn)過(guò)程

    這篇文章主要介紹了Spring整合mybatis實(shí)現(xiàn)過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2021-07-07
  • Java中List和fastjson的JSONArray相互轉(zhuǎn)換代碼示例

    Java中List和fastjson的JSONArray相互轉(zhuǎn)換代碼示例

    這篇文章主要介紹了如何在Java中將List和JSONArray相互轉(zhuǎn)換,展示了如何將List轉(zhuǎn)換為JSONArray,并且可以直接運(yùn)行這段代碼,需要的朋友可以參考下
    2025-04-04
  • java得到某年某周的第一天實(shí)現(xiàn)思路及代碼

    java得到某年某周的第一天實(shí)現(xiàn)思路及代碼

    某年某周的第一天,此功能如何使用java編程得到呢?既然有了問(wèn)題就有解決方法,感興趣的朋友可以了解下本文,或許會(huì)給你帶來(lái)意想不到的收獲哦
    2013-01-01
  • Java實(shí)現(xiàn)快速排序過(guò)程分析

    Java實(shí)現(xiàn)快速排序過(guò)程分析

    今天小編就為大家分享一篇關(guān)于Java實(shí)現(xiàn)快速排序過(guò)程分析,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2018-10-10
  • JavaWeb頁(yè)面中防止點(diǎn)擊Backspace網(wǎng)頁(yè)后退情況

    JavaWeb頁(yè)面中防止點(diǎn)擊Backspace網(wǎng)頁(yè)后退情況

    當(dāng)鍵盤(pán)敲下后退鍵(Backspace)后怎么防止網(wǎng)頁(yè)后退情況呢?今天小編通過(guò)本文給大家詳細(xì)介紹下,感興趣的朋友一起看看吧
    2016-11-11
  • idea使用mybatis插件mapper中的方法爆紅的解決方案

    idea使用mybatis插件mapper中的方法爆紅的解決方案

    這篇文章主要介紹了idea使用mybatis插件mapper中的方法爆紅的解決方案,文中給出了詳細(xì)的原因分析和解決方案,對(duì)大家解決問(wèn)題有一定的幫助,需要的朋友可以參考下
    2024-07-07
  • JDBC PreparedStatement Like參數(shù)報(bào)錯(cuò)解決方案

    JDBC PreparedStatement Like參數(shù)報(bào)錯(cuò)解決方案

    這篇文章主要介紹了JDBC PreparedStatement Like參數(shù)報(bào)錯(cuò)解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-10-10
  • 詳解SpringBoot開(kāi)發(fā)使用@ImportResource注解影響攔截器

    詳解SpringBoot開(kāi)發(fā)使用@ImportResource注解影響攔截器

    這篇文章主要介紹了詳解SpringBoot開(kāi)發(fā)使用@ImportResource注解影響攔截器,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-11-11
  • java基礎(chǔ)javeSE程序邏輯控制語(yǔ)法

    java基礎(chǔ)javeSE程序邏輯控制語(yǔ)法

    主要講解Java中程序的邏輯控制語(yǔ)句包括 Java中的輸入輸出方式 順序結(jié)構(gòu)循環(huán)結(jié)構(gòu)等,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2021-09-09

最新評(píng)論

工布江达县| 安阳市| 兴文县| 宁化县| 朝阳市| 黄冈市| 甘泉县| 乡宁县| 上杭县| 鱼台县| 西藏| 虎林市| 九江县| 内乡县| 肃南| 南皮县| 霍林郭勒市| 阿荣旗| 舒兰市| 农安县| 民丰县| 巴中市| 安化县| 繁昌县| 朝阳市| 澄城县| 定州市| 西城区| 连南| 吉林省| 法库县| 泾川县| 特克斯县| 潼关县| 博兴县| 永年县| 永州市| 朝阳市| 苍溪县| 巴林右旗| 永宁县|