spring boot 實現mybatis攔截器
項目是個報表系統,服務端是簡單的Java web架構,直接在請求參數里面加了個query id參數,就是mybatis mapper的query id,剛好對接接口的請求參數,沒有使用接口模式。
基於這種特性,分頁使用了PageHelper插件,但是只使用獲取指定范圍記錄這部分,查詢的總條數是自己實現的(插件的總條數,需要創建查詢對象實現),直接在查詢后多發送一次數據庫請求,使用自定義的攔截器,攔截查詢的請求(默認注入一個參數,等於指定值),在正常的SQL前后 拼接上 "select count(1) coun from (" +sql+")",直接在數據庫中,查詢結果集的條數。如下:
if ( mapParam.get("count") != null && mapParam.get("count").toString().equalsIgnoreCase("sum")) { // 從StatementHandler中取出查詢的SQL String sql = (String) metaStatementHandler.getValue("delegate.boundSql.sql"); sql = "select count(1) coun from ( " + sql + " )"; // 將修改的SQL放回StatementHandler中 metaStatementHandler.setValue("delegate.boundSql.sql", sql); }
mybatis的 攔截器配置如下:
<plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor"> <!-- 支持通過Mapper接口參數來傳遞分頁參數 --> <property name="supportMethodsArguments" value="true"/> <property name="rowBoundsWithCount" value="true"/> <property name="reasonable" value="true"/> </plugin> <!-- mybatis寫出sql記錄控件(攔截器) --> <!-- 自己寫的那個攔截器 --> <plugin interceptor="com.xx.SqlInterceptorCount"> <!-- 方言 --> <property name="dialect" value="oracle"/> </plugin> </plugins>
Java使用如下:
// 從數據庫查詢 result = QueryMapper.getObjFromDB(paras.get("qdi"),paras); // 查詢當前sql 的總條數 total = QueryMapper.getQdiSum(paras.get("qdi"),paras);
查 total 的時候,給paras,默認注入一個count參數。
paras.put("count", "sum");
hashMap = (HashMap)session.selectList(qdi, paras).get(0);
用起來還是很酸爽的。
--------------朴素的分割線------------------
進入主題
為迎合時代的發展,領導的召喚,項目架構改版,遷入springboot,方便統一管理(監控)
程序員就開干了啊。。。
。。。
。。。
。。。
。。。
。。。
// 程序員揮汗如雨中
幾天后,springboot的項目開發完了,但是遇到個問題,mybatis的攔截器不生效,配置如下:
mybatis: mapper-locations: classpath:mapper/*.xml
事實上,並沒有配置,不過可以加個mybatis的配置文件,像這樣,如下:
mybatis: mapper-locations: classpath:mapper/*.xml configuration: interceptions: cn.utstarcom.statplatform7.interceptor.SqlInterceptorCount config-location: classpath:mybatis-config.xml
mbatis-config.xml 內容如下:
<plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor"> <!-- 支持通過Mapper接口參數來傳遞分頁參數 --> <property name="supportMethodsArguments" value="true"/> <property name="rowBoundsWithCount" value="true"/> <property name="reasonable" value="true"/> </plugin> <!-- mybatis寫出sql記錄控件(攔截器) --> <!-- 自己寫的那個攔截器 --> <plugin interceptor="com..SqlInterceptorCount"> <!-- 方言 --> <property name="dialect" value="oracle"/> </plugin> </plugins>
攔截器內容如下:
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})}) public class SqlInterceptorCount implements Interceptor { private Logger logger = LoggerFactory.getLogger(SqlInterceptorCount.class); private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory(); private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory(); private static final ReflectorFactory DEFAULT_REFLECTOR_FACTORY = new DefaultReflectorFactory(); @Override public Object intercept(Invocation invocation) throws Throwable { try { StatementHandler statementHandler = (StatementHandler) invocation.getTarget(); MetaObject metaStatementHandler = MetaObject.forObject(statementHandler, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, DEFAULT_REFLECTOR_FACTORY); // HashMap mapParam = (HashMap) metaStatementHandler.getValue("delegate.boundSql.parameterObject"); Object paramObject = metaStatementHandler.getValue("delegate.boundSql.parameterObject"); if (paramObject instanceof Map) { HashMap paramMap = (HashMap)paramObject; if (paramMap.get("count") != null && paramMap.get("count").toString().equalsIgnoreCase("sum")) { // get sql from StatementHandler String sql = (String) metaStatementHandler.getValue("delegate.boundSql.sql"); sql = "select count(1) coun from ( " + sql + " )"; // set moidfy sql to StatementHandler metaStatementHandler.setValue("delegate.boundSql.sql", sql); } } } catch (Exception e) { logger.error("throw exception and will not handle the mybatis sql"); } // process the next query process return invocation.proceed(); } /** * * @param o * @return */ @Override public Object plugin(Object o) { return null; } @Override public void setProperties(Properties properties) { } }
事實上,mybatis的配置文件和配置的 interceptions,不能使用(我當然知道是),啟動沒有問題,數據庫也可以操作,但是攔截器沒有生效。
1、在又是一大圈的亂找,發現個說法,在攔截器上加個 @Component 注解就可以了。
然后,還是不行,這個更慘,只要查詢數據庫,都會返回個null ,又沒有報錯信息,只是個null
2、又是網上一通亂找,看到個說法:說攔截器只有 plugin方法執行了,intercept方法沒有執行
debug 一下,發現問題了,加上@Component 注解,plugin方法執行了,返回個null,知道為什么嗎,看下plugin的代碼:
@Override public Object plugin(Object o) { return null; }
我去
不返回null,返回什么
不返回null,返回什么
不返回null,返回什么
之后接簡單了,plugin 方法是實現 Interceptor 接口的時候,IDEA生成的方法,有個默認實現,返回null
改改:
@Override public Object plugin(Object o) { // logger.info("Interceptor : SqlInterceptorCount is running, but this is plugin method not intercept method"); /* use Intercepts annotation,intercept StatementHandler method prepare */ if (o instanceof StatementHandler) { return Plugin.wrap(o, this); } return o; }
在傳入對象是 STATEMENTHandler的對象的時候,把方法轉發到intercept 方法,攔截器可以正常執行了。
結論就是:
springboot下的mybatis攔截器,和單獨用mybatis的攔截器一樣,但是不需要配置,直接在攔截器前面加個@Component 注解,在plugin 方法中將請求轉發到intercept方法攔截即可,不需要其他配置。
完整代碼如下:
@Component @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})}) public class SqlInterceptorCount implements Interceptor { private Logger logger = LoggerFactory.getLogger(SqlInterceptorCount.class); private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory(); private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory(); private static final ReflectorFactory DEFAULT_REFLECTOR_FACTORY = new DefaultReflectorFactory(); @Override public Object intercept(Invocation invocation) throws Throwable { try { StatementHandler statementHandler = (StatementHandler) invocation.getTarget(); MetaObject metaStatementHandler = MetaObject.forObject(statementHandler, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, DEFAULT_REFLECTOR_FACTORY); // HashMap mapParam = (HashMap) metaStatementHandler.getValue("delegate.boundSql.parameterObject"); Object paramObject = metaStatementHandler.getValue("delegate.boundSql.parameterObject"); if (paramObject instanceof Map) { HashMap paramMap = (HashMap)paramObject; if (paramMap.get("count") != null && paramMap.get("count").toString().equalsIgnoreCase("sum")) { // get sql from StatementHandler String sql = (String) metaStatementHandler.getValue("delegate.boundSql.sql"); sql = "select count(1) coun from ( " + sql + " )"; // set moidfy sql to StatementHandler metaStatementHandler.setValue("delegate.boundSql.sql", sql); } } } catch (Exception e) { logger.error("throw exception and will not handle the mybatis sql"); } // process the next query process return invocation.proceed(); } /** * * @param o * @return */ @Override public Object plugin(Object o) { // logger.info("Interceptor : SqlInterceptorCount is running, but this is plugin method not intercept method"); /* use Intercepts annotation,intercept StatementHandler method prepare */ if (o instanceof StatementHandler) { return Plugin.wrap(o, this); } return o; } @Override public void setProperties(Properties properties) { } }