mybatis攔截器(下)


mybatis若想實現自定義攔截器,需要實現Interceptor接口,對象首先會執行plugin(Object target)方法,根據類上的@Intercepts注解決定是否攔截。若需要攔截,則調用intercept(Invocation invocation)方法。

1. 准備工作

需要攔截的sql:

  <select id="selectByNameAndGroup" parameterType="java.lang.String" resultMap="BaseResultMap"> select <include refid="Base_Column_List" /> from sys_quartz_job_config where job_Name = #{jobName,jdbcType=VARCHAR} AND job_Group =#{jobGroup,jdbcType=VARCHAR} </select> 

需要攔截的Mapper對象:

SysQuartzJobConfig selectByNameAndGroup(

invocation對象:

 
invocation對象

可以看到invocation中的args參數,就是@Intercepts中的args參數。

@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})}) 

2. mappedStatement對象

1. invocation對象如何獲取mappedStatement對象:

一個mappedStatement對象對應Mapper配置文件中的一個select/update/insert/delete節點,主要描述的是一條sql語句。

//獲取參數1:MappedStatement對象 MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0]; 
 
MappedStatement.png

2. MappedStatement對象詳解:

public final class MappedStatement { //該Mapper.xml的絕對路徑 private String resource; //mybatis所有的配置 private Configuration configuration; //sql的ID(命令空間+key) private String id; //嘗試影響驅動程序每次批量返回的結果行數和這個設置值相等 private Integer fetchSize; //SQL超時時間 private Integer timeout; //Statement的類型,STATEMENT/PREPARE/CALLABLE private StatementType statementType; //結果集類型,FORWARD_ONLY/SCROLL_SENSITIVE/SCROLL_INSENSITIVE private ResultSetType resultSetType; //表示解析出來的SQL private SqlSource sqlSource; //緩存 private Cache cache; //已廢棄 private ParameterMap parameterMap; //對應的ResultMap private List<ResultMap> resultMaps; private boolean flushCacheRequired; private boolean useCache; private boolean resultOrdered; //SQL類型,INSERT/SELECT/DELETE private SqlCommandType sqlCommandType; //和SELECTKEY標簽有關 private KeyGenerator keyGenerator; private String[] keyProperties; private String[] keyColumns; private boolean hasNestedResultMaps; //數據庫ID,用來區分不同環境 private String databaseId; private Log statementLog; private LanguageDriver lang; //多結果集時 private String[] resultSets; MappedStatement() { // constructor disabled } ... } 

其中真正表示SQL的字段是SqlSource這個對象。
而SqlSource接口很簡單,只有一個getBoundsql方法。

public interface SqlSource { BoundSql getBoundSql(Object parameterObject); } 

sqlSource有很多實現,需要我們重點關注的是StaticSqlSource,RawSqlSource和DynamicSqlSource。而通過上述的實現,便可以將mybatis特有的sql格式轉化成可供PrepareStatement直接執行的sql。

mappedStatement —— BoundSql對象:

 BoundSql boundSql = mappedStatement.getBoundSql(parameter);

上述方法主要是對動態標簽的解析,獲取完全可執行的sql。對#{ }字符解析,將其替換成?,最后均包裝成域表達式供PrepareStatement調用。

  • 解析后的sql保存在sql對象中;
  • 請求參數保存在parameterObject對象中;
  • #{ }key屬性以及相對應的參數映射,比如javaType,jdbcType等信息均保存至BoundSql的parameterMapping屬性中。供最后的域表達式對象PrepareStatement賦值使用。
 
BoundSql .png

BoundSql—解析后的sql對象:

BoundSql對象中的sql對象是對動態標簽解析后的完全可執行的sql。

select ID, JOB_NAME, JOB_GROUP, ENABLE, CLASS_NAME, CRON, CONCURRENT, CREATE_TIME, MODIFY_TIME from sys_quartz_job_config where job_Name = ? AND job_Group =? 

BoundSql—ParameterObject對象:

該對象為sql執行的參數。也就是我們傳入的參數。因為使用了@param注解,故有兩種方式可以獲取到value的值。

//獲取parameterObject對象 Object parameter = null; if (invocation.getArgs().length > 1) { parameter = invocation.getArgs()[1]; } 
 
parameterObject.png

注意:ParameterObject是一個Object對象,上傳不同的參數時,該對象的類型不同。

  • 若上傳一個參數對象時:為該參數的類型;
  • 若上傳多個參數對象時:為ParamMap對象,由於我們使用了@Param注解,故可以使用注解的key,也可以使用param1取出變量。
  • 若上傳的為Criteria對象時(如下圖):也是為該對象的類型,但是獲取對象的方法又不相同。
 
Criteria對象.png

注意:如果真實傳遞進來的參數在TypeHandlerRegistry對象中聲明的話,則使用真實傳遞進來的參數作為真實的變量名。

換句話說,我們在Mapper接口里面寫delete(Integer id),而在Mapper.xml中定義的變量#{var}可以隨便命名,都可以被mybatis正確處理。

BoundSql—ParameterMapping對象:

采用#{var}的形式來引用變量時,其中的變量會在解析Mapper.xml文件中的語句時,就被替換成占位符“?”,同時通過ParameterMapping類記錄對應的變量信息。在真正執行對應語句的時候回傳遞真實的參數。根據parameterMapping信息給ParameterStatement設置參數。

 
Criteria對象的ParameterMapping對象.png
 
普通對象的ParameterMapping對象.png

需要注意的是,property參數就是#{var}中的var。

BoundSql—additionalParameters對象

若是使用Criteria對象時的sql,那么在additionalParameters中便有值,我們可以使用:

//獲取請求參數 //獲取#{var}中的key String propertyName = parameterMapping.getProperty(); if (boundSql.hasAdditionalParameter(propertyName)) { Object obj = boundSql.getAdditionalParameter(propertyName); } 
 
Criteria對象的additionalParameters參數.png

BoundSql—MetaObject對象

MetaObject類相當於一個工具類,Mybatis在sql參數設置和結果集映射里經常使用到這個對象。

 //原始的對象 private Object originalObject; //對原始對象的一個包裝 private ObjectWrapper objectWrapper; //這兩個屬性基本不用,因為在Mybatis中都找不到ObjectWrapperFactory的有效實現類 private ObjectFactory objectFactory; private ObjectWrapperFactory objectWrapperFactory; 
 
MetaObject.png
//通過MetaObject完成參數的設置 //獲取key String propertyName = parameterMapping.getProperty(); if (metaObject.hasGetter(propertyName)) { Object obj = metaObject.getValue(propertyName); } 

MappedStatement——Configuration對象

mybatis會在啟動時讀取所有的配置文件,然后加載到內存中,Configuration對象就是承載整個配置的類。

Configuration configuration = mappedStatement.getConfiguration();
public class Configuration { /** * MyBatis 可以配置成適應多種環境,這種機制有助於將 SQL 映射應用於多種數據庫之中, * 比如設置不同的開發、測試、線上配置,在每個配置中可以配置事務管理器和數據源對象. */ protected Environment environment; //允許在嵌套語句中使用分頁(RowBounds)。如果允許使用則設置為false。 protected boolean safeRowBoundsEnabled = false; //允許在嵌套語句中使用分頁(ResultHandler)。如果允許使用則設置為false protected boolean safeResultHandlerEnabled = true; //是否開啟自動駝峰命名規則(camel case)映射,即從經典數據庫列名 A_COLUMN 到經典 Java 屬性名 aColumn 的類似映射。 protected boolean mapUnderscoreToCamelCase = false; //當開啟時,任何方法的調用都會加載該對象的所有屬性。否則,每個屬性會按需加載(參考lazyLoadTriggerMethods). protected boolean aggressiveLazyLoading = true; //是否允許單一語句返回多結果集(需要兼容驅動) protected boolean multipleResultSetsEnabled = true; //允許 JDBC 支持自動生成主鍵,需要驅動兼容。 如果設置為 true 則這個設置強制使用自動生成主鍵,盡管一些驅動不能兼容但仍可正常工作(比如 Derby)。 protected boolean useGeneratedKeys = false; //使用列標簽代替列名。不同的驅動在這方面會有不同的表現, 具體可參考相關驅動文檔或通過測試這兩種不同的模式來觀察所用驅動的結果。 protected boolean useColumnLabel = true; //配置全局性的cache開關 protected boolean cacheEnabled = true; /*指定當結果集中值為 null 的時候是否調用映射對象的 setter(map 對象時為 put)方法,這對於有 Map.keySet() 依賴或 null 值初始化的時候是有用的。 注意基本類型(int、boolean等)是不能設置成 null 的。*/ protected boolean callSettersOnNulls = false; //指定 MyBatis 增加到日志名稱的前綴。 protected String logPrefix; //指定 MyBatis 所用日志的具體實現,未指定時將自動查找 protected Class <? extends Log> logImpl; /*MyBatis 利用本地緩存機制(Local Cache)防止循環引用(circular references)和加速重復嵌套查詢。 默認值為 SESSION,這種情況下會緩存一個會話中執行的所有查詢。 若設置值為 STATEMENT,本地會話僅用在語句執行上,對相同 SqlSession 的不同調用將不會共享數據。*/ protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION; /*當沒有為參數提供特定的 JDBC 類型時,為空值指定 JDBC 類型。 某些驅動需要指定列的 JDBC 類型,多數情況直接用一般類型即可,比如 NULL、VARCHAR 或 OTHER。*/ protected JdbcType jdbcTypeForNull = JdbcType.OTHER; //指定哪個對象的方法觸發一次延遲加載。 protected Set<String> lazyLoadTriggerMethods = new HashSet<String>(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" })); //設置超時時間,它決定驅動等待數據庫響應的秒數。 

Configuration — TypeHandlerRegistry對象

類型處理器注冊對象。在構建TypeHandlerRegistry對象的時候,便將類型注冊了進去。

類型處理器TypeHandlerRegistry簡單點就是用於處理javaType與jdbcType之間的類型轉換用的處理器,Mybatis針對諸多Java類型與數據庫類型進行了匹配處理。

//獲取到類型處理器 TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry(); 

類型處理器的格式:

public TypeHandlerRegistry() { register(Boolean.class, new BooleanTypeHandler()); register(boolean.class, new BooleanTypeHandler()); register(JdbcType.BOOLEAN, new BooleanTypeHandler()); register(JdbcType.BIT, new BooleanTypeHandler()); }


作者:小胖學編程
鏈接:https://www.jianshu.com/p/8440d1d66608
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM