一、Mybatis攔截器介紹
Mybatis攔截器設計的思路是為了供用戶靈活的實現自己的邏輯,而不動mybatis固有的邏輯,簡而言之就是如果Mybatis是一只蝦,我們要做的是將蝦肉掏空,放入自己喜歡吃的東西進去,而依舊保持嚇得殼身。通過Mybatis攔截器我們能攔截某些方法的調用,我們可以選擇在這些被攔截方法執行前后加上我們自己的邏輯,達到豐富方法的效果;也可以在執行這些方法的時候攔截住,轉而實現自己設計的方法,而最后不執行被攔截的方法。Mybatis中有很多核心對象,大致如下:
SqlSession | 負責和數據庫進行交互,實現完成常對數據操作的增刪改查功能 |
Executor | Mybatis執行器,調度的核心,負責SQL語句的生成和維護 |
StatementHandler | 封裝並操作Jdbc statement,例如設置參數,將statement結果集轉化為List集合 |
ParameterHandler | 負責將用戶傳遞的參數轉換為Jdbc statement所需要的參數進行傳遞 |
ResultSetHandler | 負責將Jdbc返回的ResultSet結果集對象轉換為List類型的集合 |
TypeHandler | 負責將java數據類型和Jdbc數據類型之間的轉換和映射 |
MappedStatement | MappedStatement維護了一條mapper.xml文件里面 select 、update、delete、insert節點的封裝 |
Configuration | MyBatis所有的配置信息都維持在Configuration對象之中 |
BoundSql | 表示動態生成的SQL語句以及相應的參數信息 |
Mybatis攔截器並不能攔截mybatis核心對象中所有方法,相反的,MyBatis攔截器可以攔截四個對象中方法,分別是:Executor、StatementHandler、ParameterHandler 和ResultSetHandler。
1. Executor
Executor是mybatis核心接口,數據庫增刪改語句是通過Executor接口的update方法進行的,查詢是通過query方法進行,代碼如下:
public interface Executor { /* * 執行update/insert/delete * */ int update(MappedStatement ms , Object paramter)throws SQLException; /* * 執行查詢,先在緩存中查找 * Mybatis提供了一個簡單的邏輯分頁使用類RowBounds(物理分頁當然就是我們在sql語句中指定limit和offset值),在DefaultSqlSession提供的某些查詢接口中我們可以看到RowBounds是作為參數用來進行分頁的 * */ <E> List<E> query(MappedStatement ms , Object paramter , RowBounds rowBounds , ResultSetHandler resultSetHandler , CacheKey cacheKey , BoundSql boundSql)throws SQLException; /* * 執行查詢 * */ <E> List<E> query(MappedStatement ms , Object paramter , ResultSetHandler resultSetHandler)throws SQLException; /* * 執行查詢,結果放在Cursor里面 * */ <E> Cursor<E> queryCursor(MappedStatement ms , Object paramter)throws SQLException; }
2. ParameterHandler
ParameterHandler用來設置參數規則,當StatementHandler使用prepare()方法后,接下來就是使用它來設置參數。所以如果有對參數做自定義邏輯處理的時候,可以通過攔截ParameterHandler來實現。ParameterHandler里面可以攔截的方法解釋如下:
public interface ParameterHandler { /* * 設置參數規則的時候使用——PreparedStatement * */ void setParamters(PreparedStatement ps) throws SQLException; }
3. StatementHandler
StatementHandler負責處理Mybatis與JDBC之間Statement的交互。
public interface StatementHandler { /* * 從連接中獲取一個Statement * */ Statement prepare(Connection conn , Integer transactionTimeout)throws SQLException; /* * 設置statement執行里需要的參數 * */ void paramterize(Statement statement)throws SQLException; /** * 批量 */ void batch(Statement statement) throws SQLException; /** * 更新:update/insert/delete語句 */ int update(Statement statement) throws SQLException; /** * 執行查詢 */ <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException; <E> Cursor<E> queryCursor(Statement statement) throws SQLException; }
一般只攔截prepare方法。
在Mybatis里面RoutingStatementHandler是SimpleStatementHandler(對應Statement)、PreparedStatementHandler(對應PreparedStatement)、CallableStatementHandler(對應CallableStatement)的路由類,所有需要攔截StatementHandler里面的方法的時候,對RoutingStatementHandler做攔截處理就可以了,如下的寫法可以過濾掉一些不必要的攔截類。
@Intercepts({ @Signature( type = Statement.class , method = "prepare", args = {Connection.class , Integer.class} ) }) public class TableShardInterceptor implements Interceptor { /* * mybatis運行時要執行的攔截方法 * */ @Override public Object intercept(Invocation invocation ) throws Throwable{ if(invocation.getTarget()instanceof RoutingStatementHandler){ //to do 自己的邏輯 } return invocation.proceed(); } /* * target:攔截對象 * * */ @Override public Object plugin(Object target){ // 當目標類是StatementHandler類型時,才包裝目標類,否者直接返回目標本身,減少目標被代理的次數 return (target instanceof RoutingStatementHandler)? Plugin.wrap(target,this):target; } //實現插件參數傳遞 @Override public void setProperties(Properties properties){ } }
4. ResultSetHandler
ResultSetHandler用於對查詢到的結果做處理。所以如果你有需求需要對返回結果做特殊處理的情況下可以去攔截ResultSetHandler的處理。ResultSetHandler里面常用攔截方法如下:
public interface ResultSetHandler { /* * 將statement執行后產生的結果集(可能有多個結果集)映射為結果表 * */ <E> List<E> handResultSets(Statement stmt)throws SQLException; <E> List<E> handleCursorResultSet(Statement stmt)throws SQLException; /* * 處理存儲過程執行后的輸出參數 * */ void handleOutputParameters(CallableStatement cs) throws SQLException; }
至此,攔截器介紹由此結束。
部分內容參考自:https://blog.csdn.net/wuyuxing24/article/details/89343951