天天看點

Mybatis——攔截器Interceptor

作者:IT楓鬥者

一,什麼是Mybatis攔截器?它的作用是什麼?

MyBatis允許使用者在映射語句執行過程中的某一些指定的節點進行攔截調用,通過織入攔截器,在不同節點修改一些執行過程中的關鍵屬性,進而影響SQL的生成、執行和傳回結果,如:來影響Mapper.xml到SQL語句的生成、執行SQL前對預編譯的SQL執行參數的修改、SQL執行後傳回結果到Mapper接口方法返參POJO對象的類型轉換和封裝等。(IT楓鬥者怎麼樣)

作用:

最常見的就是Mybatis自帶的分頁插件PageHelper或Rowbound參數,通過列印實際執行的SQL語句,發現我們的分頁查詢之前,先執行了COUNT(*)語句查詢數量,然後再執行查詢時修改了SQL語句即在我們寫的SQL語句後拼接上了分頁語句LIMIT(offset, pageSize)。(IT楓鬥者怎麼樣)

二,Mybatis的執行過程是怎樣的?

首先了解它的核心對象

主要包括:

  • Configuration:初始化基礎配置,比如MyBatis的别名等,一些重要的類型對象,如插件,映射器,ObjectFactory和typeHandler對象,MyBatis所有的配置資訊都維持在Configuration對象之中。
  • SqlSessionFactory:SqlSession工廠。
  • SqlSession:作為MyBatis工作的主要頂層API,表示和資料庫互動的會話,完成必要的資料庫增删改查功能。
  • Executor:MyBatis的内部執行器,它負責調用StatementHandler操作資料庫,并把結果集通過ResultSetHandler進行自動映射,另外,它還處理二級緩存的操作。
  • StatementHandler:MyBatis直接在資料庫執行SQL腳本的對象。另外它也實作了MyBatis的一級緩存。(IT楓鬥者怎麼樣)
  • ParameterHandler:負責将使用者傳遞的參數轉換成JDBC Statement所需要的參數。是MyBatis實作SQL入參設定的對象。
  • ResultSetHandler:負責将JDBC傳回的ResultSet結果集對象轉換成List類型的集合。是MyBatis把ResultSet集合映射成POJO的接口對象。
  • TypeHandler:負責Java資料類型和JDBC資料類型之間的映射和轉換。
  • MappedStatement:MappedStatement維護了一條<select|update|delete|insert>節點的封裝。(IT楓鬥者怎麼樣)
  • SqlSource :負責根據使用者傳遞的parameterObject,動态地生成SQL語句,将資訊封裝到BoundSql對象中,并傳回。
  • BoundSql:表示動态生成的SQL語句以及相應的參數資訊。

其次它的執行過程

詳細的執行過程可以先看下圖:

Mybatis——攔截器Interceptor

延伸:為什麼不能通過ParameterHandler?而是用mybatis的<if>标簽動态修改SQL語句呢?

在執行ParameterHandler之前,BoundSQL對象已經生成了,即預編譯的SQL語句已經生成了;可見,BoundSql生成SQL語句的時機在于 statementHandler的建立,由MappedStatementHandler.getBoundSql(Object parameterObject)函數解析動态标簽,生成靜态SQL語句,早于parameterHandler和ResultSetHandler的時機;是以,隻能通過影響parameterObject(暫時先将其看成一個Map,鍵值分别為參數名和參數值)的值,來幹預對Mapper.xml标簽的解析過程,進而改變生成的BoundSql對象。(IT楓鬥者怎麼樣)

最後如何使用Mybatis攔截器?

自定義一個mybatis的攔截器步驟包含:

  • 定義一個實作org.apache.ibatis.plugin.Interceptor接口的攔截器類,并實作其中的方法。
  • 添加@Intercepts注解,寫上需要攔截的對象和方法,以及方法參數。
  • 在mybatis的全局配置xml中配置插件plugin;對于去xml的Spring工程,顯示的注冊這個攔截器Bean即可。(IT楓鬥者怎麼樣)

1、添加注解

MyBatis攔截器預設可以攔截的類型隻有四種,即四種接口類型Executor、

StatementHandler、ParameterHandler和ResultSetHandler。對于我們的自定義攔截器必須使用MyBatis提供的@Intercepts注解來指明我們要攔截的是四種類型中的哪一種接口。

1.1、 @Signature注解

  • @Intercepts // 描述:标志該類是一個攔截器
  • @Signature // 描述:指明該攔截器需要攔截哪一個接口的哪一個方法

1.2、@Signature注解中的type屬性

  • type; // 四種類型接口中的某一個接口,如Executor.class;
  • method; // 對應接口中的某一個方法名,比如Executor的query方法;
  • args; // 對應接口中的某一個方法的參數,比如Executor中query方法因為重載原因,有多個,args就是指明參數類型,進而确定是具體哪一個方法;(IT楓鬥者怎麼樣)

MyBatis攔截器預設會按順序攔截以下的四個接口中的所有方法:

  • org.apache.ibatis.executor.Executor //攔截執行器方法
  • org.apache.ibatis.executor.statement.StatementHandler //攔截SQL文法建構處理
  • org.apache.ibatis.executor.parameter.ParameterHandler //攔截參數處理
  • org.apache.ibatis.executor.resultset.ResultSetHandler //攔截結果集處理

實際上,具體是攔截這四個接口對應的實作類:

  • org.apache.ibatis.executor.CachingExecutor
  • org.apache.ibatis.executor.statement.RoutingStatementHandler
  • org.apache.ibatis.scripting.defaults.DefaultParameterHandler
  • org.apache.ibatis.executor.resultset.DefaultResultSetHandler

2、實作org.apache.ibatis.plugin.Interceptor接口

實作Interceptor接口,主要是實作下面幾個方法:intercept(Invocation invocation)、plugin(Object target) 、setProperties(Properties properties);(IT楓鬥者怎麼樣)

2.1、intercept方法

進行攔截的時候要執行的方法。該方法參數Invocation類中有三個字段:

  • private final Object target;
  • private final Method method;
  • private final Object[] args;

可通過這三個字段分别擷取下面的資訊:

  • Object target = invocation.getTarget(); //被代理對象
  • Method method = invocation.getMethod(); //代理方法
  • Object[] args = invocation.getArgs(); //方法參數

方法實作的結尾處,一般要真正調用被代理類的方法,即"return invocation.proceed()。"

2.2、 plugin方法

插件用于封裝目标對象的,通過該方法我們可以傳回目标對象本身,也可以傳回一個它的代理,可以決定是否要進行攔截進而決定要傳回一個什麼樣的目标對象,官方提供了示例:"return Plugin.wrap(target, this);";(IT楓鬥者怎麼樣)

Tips:可以在這個方法中提前進行攔截對象類型判斷進而提高性能,如我們僅對Executor攔截,可以這麼寫:

Mybatis——攔截器Interceptor
Mybatis——攔截器Interceptor

2.3、setProperties方法

如果我們攔截器需要用到一些變量參數,而且這個參數是支援可配置的,類似Spring中的@Value("${}")從application.properties檔案擷取自定義變量屬性,這個時候我們就可以使用這個方法。(IT楓鬥者怎麼樣)

參考下方代碼,在Mybatis配置檔案中配置插件時可以設定參數,在setProperties函數中調用 Properties.getProperty("param1") 方法可以得到配置的值:

Mybatis——攔截器Interceptor

(IT楓鬥者怎麼樣)

實際上是解析XML,将XML中注冊的攔截器的配置帶過來。