mybatis(4)—自定義攔截器(下)對(duì)象詳解

mybatis自定義攔截器(一)基本使用
mybatis自定義攔截器(二)對(duì)象詳解

mybatis若想實(shí)現(xiàn)自定義攔截器,需要實(shí)現(xiàn)Interceptor接口垦搬,對(duì)象首先會(huì)執(zhí)行plugin(Object target)方法网严,根據(jù)類上的@Intercepts注解決定是否攔截识樱。若需要攔截,則調(diào)用intercept(Invocation invocation)方法震束。

1. 準(zhǔn)備工作

需要攔截的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對(duì)象:

SysQuartzJobConfig selectByNameAndGroup(@Param("jobName") String jobName,@Param("jobGroup") String jobGroup);

invocation對(duì)象:

invocation對(duì)象

可以看到invocation中的args參數(shù)怜庸,就是@Intercepts中的args參數(shù)。

@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對(duì)象

1. invocation對(duì)象如何獲取mappedStatement對(duì)象:

一個(gè)mappedStatement對(duì)象對(duì)應(yīng)Mapper配置文件中的一個(gè)select/update/insert/delete節(jié)點(diǎn)垢村,主要描述的是一條sql語句割疾。

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

2. MappedStatement對(duì)象詳解:

public final class MappedStatement {
  //該Mapper.xml的絕對(duì)路徑
  private String resource;
  //mybatis所有的配置
  private Configuration configuration;
  //sql的ID(命令空間+key)
  private String id;
  //嘗試影響驅(qū)動(dòng)程序每次批量返回的結(jié)果行數(shù)和這個(gè)設(shè)置值相等
  private Integer fetchSize;
  //SQL超時(shí)時(shí)間
  private Integer timeout;
  //Statement的類型,STATEMENT/PREPARE/CALLABLE
  private StatementType statementType;
  //結(jié)果集類型嘉栓,F(xiàn)ORWARD_ONLY/SCROLL_SENSITIVE/SCROLL_INSENSITIVE 
  private ResultSetType resultSetType;
  //表示解析出來的SQL
  private SqlSource sqlSource;
  //緩存
  private Cache cache;
  //已廢棄
  private ParameterMap parameterMap;
  //對(duì)應(yīng)的ResultMap
  private List<ResultMap> resultMaps;
  private boolean flushCacheRequired;
  private boolean useCache;
  private boolean resultOrdered;
  //SQL類型宏榕,INSERT/SELECT/DELETE
  private SqlCommandType sqlCommandType;
  //和SELECTKEY標(biāo)簽有關(guān)
  private KeyGenerator keyGenerator;
  private String[] keyProperties;
  private String[] keyColumns;
  private boolean hasNestedResultMaps;
  //數(shù)據(jù)庫(kù)ID驰凛,用來區(qū)分不同環(huán)境
  private String databaseId;
  private Log statementLog;
  private LanguageDriver lang;
  //多結(jié)果集時(shí)
  private String[] resultSets;

  MappedStatement() {
    // constructor disabled
  }
  ...
  }

其中真正表示SQL的字段是SqlSource這個(gè)對(duì)象。
而SqlSource接口很簡(jiǎn)單担扑,只有一個(gè)getBoundsql方法恰响。

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

sqlSource有很多實(shí)現(xiàn),需要我們重點(diǎn)關(guān)注的是StaticSqlSource涌献,RawSqlSource和DynamicSqlSource胚宦。而通過上述的實(shí)現(xiàn),便可以將mybatis特有的sql格式轉(zhuǎn)化成可供PrepareStatement直接執(zhí)行的sql燕垃。

mappedStatement —— BoundSql對(duì)象:

 BoundSql boundSql = mappedStatement.getBoundSql(parameter);

上述方法主要是對(duì)動(dòng)態(tài)標(biāo)簽的解析蚯涮,獲取完全可執(zhí)行的sql去枷。對(duì)#{ }字符解析,將其替換成?赢织,最后均包裝成域表達(dá)式供PrepareStatement調(diào)用僻造。

  • 解析后的sql保存在sql對(duì)象中废亭;
  • 請(qǐng)求參數(shù)保存在parameterObject對(duì)象中百炬;
  • #{ }key屬性以及相對(duì)應(yīng)的參數(shù)映射,比如javaType侦副,jdbcType等信息均保存至BoundSql的parameterMapping屬性中侦锯。供最后的域表達(dá)式對(duì)象PrepareStatement賦值使用。
BoundSql .png

BoundSql—解析后的sql對(duì)象:

BoundSql對(duì)象中的sql對(duì)象是對(duì)動(dòng)態(tài)標(biāo)簽解析后的完全可執(zhí)行的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對(duì)象:

該對(duì)象為sql執(zhí)行的參數(shù)尺碰。也就是我們傳入的參數(shù)。因?yàn)槭褂昧薂param注解译隘,故有兩種方式可以獲取到value的值亲桥。

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

注意:ParameterObject是一個(gè)Object對(duì)象,上傳不同的參數(shù)時(shí)固耘,該對(duì)象的類型不同题篷。

  • 若上傳一個(gè)參數(shù)對(duì)象時(shí):為該參數(shù)的類型;
  • 若上傳多個(gè)參數(shù)對(duì)象時(shí):為ParamMap對(duì)象玻驻,由于我們使用了@Param注解悼凑,故可以使用注解的key偿枕,也可以使用param1取出變量璧瞬。
  • 若上傳的為Criteria對(duì)象時(shí)(如下圖):也是為該對(duì)象的類型,但是獲取對(duì)象的方法又不相同渐夸。
Criteria對(duì)象.png

注意:如果真實(shí)傳遞進(jìn)來的參數(shù)在TypeHandlerRegistry對(duì)象中聲明的話嗤锉,則使用真實(shí)傳遞進(jìn)來的參數(shù)作為真實(shí)的變量名。

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

BoundSql—ParameterMapping對(duì)象:

采用#{var}的形式來引用變量時(shí)访诱,其中的變量會(huì)在解析Mapper.xml文件中的語句時(shí)垫挨,就被替換成占位符“?”触菜,同時(shí)通過ParameterMapping類記錄對(duì)應(yīng)的變量信息九榔。在真正執(zhí)行對(duì)應(yīng)語句的時(shí)候回傳遞真實(shí)的參數(shù)。根據(jù)parameterMapping信息給ParameterStatement設(shè)置參數(shù)涡相。

Criteria對(duì)象的ParameterMapping對(duì)象.png
普通對(duì)象的ParameterMapping對(duì)象.png

需要注意的是哲泊,property參數(shù)就是#{var}中的var。

BoundSql—additionalParameters對(duì)象

若是使用Criteria對(duì)象時(shí)的sql催蝗,那么在additionalParameters中便有值切威,我們可以使用:

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

BoundSql—MetaObject對(duì)象

MetaObject類相當(dāng)于一個(gè)工具類,Mybatis在sql參數(shù)設(shè)置和結(jié)果集映射里經(jīng)常使用到這個(gè)對(duì)象丙号。

 //原始的對(duì)象
  private Object originalObject;
  //對(duì)原始對(duì)象的一個(gè)包裝
  private ObjectWrapper objectWrapper;
  
  //這兩個(gè)屬性基本不用先朦,因?yàn)樵贛ybatis中都找不到ObjectWrapperFactory的有效實(shí)現(xiàn)類
  private ObjectFactory objectFactory;
  private ObjectWrapperFactory objectWrapperFactory;
MetaObject.png
//通過MetaObject完成參數(shù)的設(shè)置
//獲取key
 String propertyName = parameterMapping.getProperty();
 if (metaObject.hasGetter(propertyName)) {
     Object obj = metaObject.getValue(propertyName);
}

MappedStatement——Configuration對(duì)象

mybatis會(huì)在啟動(dòng)時(shí)讀取所有的配置文件,然后加載到內(nèi)存中犬缨,Configuration對(duì)象就是承載整個(gè)配置的類烙无。

Configuration configuration = mappedStatement.getConfiguration();
public class Configuration {
  /**
   * MyBatis 可以配置成適應(yīng)多種環(huán)境,這種機(jī)制有助于將 SQL 映射應(yīng)用于多種數(shù)據(jù)庫(kù)之中,
   * 比如設(shè)置不同的開發(fā)遍尺、測(cè)試截酷、線上配置,在每個(gè)配置中可以配置事務(wù)管理器和數(shù)據(jù)源對(duì)象.
   */
  protected Environment environment;
 
  //允許在嵌套語句中使用分頁(yè)(RowBounds)乾戏。如果允許使用則設(shè)置為false迂苛。
  protected boolean safeRowBoundsEnabled = false; 
  //允許在嵌套語句中使用分頁(yè)(ResultHandler)。如果允許使用則設(shè)置為false
  protected boolean safeResultHandlerEnabled = true;
  //是否開啟自動(dòng)駝峰命名規(guī)則(camel case)映射鼓择,即從經(jīng)典數(shù)據(jù)庫(kù)列名 A_COLUMN 到經(jīng)典 Java 屬性名 aColumn 的類似映射三幻。
  protected boolean mapUnderscoreToCamelCase = false; 
  //當(dāng)開啟時(shí),任何方法的調(diào)用都會(huì)加載該對(duì)象的所有屬性呐能。否則念搬,每個(gè)屬性會(huì)按需加載(參考lazyLoadTriggerMethods).
  protected boolean aggressiveLazyLoading = true;
  //是否允許單一語句返回多結(jié)果集(需要兼容驅(qū)動(dòng))
  protected boolean multipleResultSetsEnabled = true; 
  //允許 JDBC 支持自動(dòng)生成主鍵,需要驅(qū)動(dòng)兼容摆出。 如果設(shè)置為 true 則這個(gè)設(shè)置強(qiáng)制使用自動(dòng)生成主鍵朗徊,盡管一些驅(qū)動(dòng)不能兼容但仍可正常工作(比如 Derby)。
  protected boolean useGeneratedKeys = false;
  //使用列標(biāo)簽代替列名偎漫。不同的驅(qū)動(dòng)在這方面會(huì)有不同的表現(xiàn)爷恳, 具體可參考相關(guān)驅(qū)動(dòng)文檔或通過測(cè)試這兩種不同的模式來觀察所用驅(qū)動(dòng)的結(jié)果。
  protected boolean useColumnLabel = true;
  //配置全局性的cache開關(guān)
  protected boolean cacheEnabled = true;
  /*指定當(dāng)結(jié)果集中值為 null 的時(shí)候是否調(diào)用映射對(duì)象的 setter(map 對(duì)象時(shí)為 put)方法象踊,這對(duì)于有 Map.keySet() 依賴或 null 值初始化的時(shí)候是有用的温亲。
    注意基本類型(int棚壁、boolean等)是不能設(shè)置成 null 的。*/
  protected boolean callSettersOnNulls = false;
  //指定 MyBatis 增加到日志名稱的前綴栈虚。
  protected String logPrefix;
  //指定 MyBatis 所用日志的具體實(shí)現(xiàn)袖外,未指定時(shí)將自動(dòng)查找
  protected Class <? extends Log> logImpl;
  /*MyBatis 利用本地緩存機(jī)制(Local Cache)防止循環(huán)引用(circular references)和加速重復(fù)嵌套查詢。 
    默認(rèn)值為 SESSION魂务,這種情況下會(huì)緩存一個(gè)會(huì)話中執(zhí)行的所有查詢在刺。 
    若設(shè)置值為 STATEMENT,本地會(huì)話僅用在語句執(zhí)行上头镊,對(duì)相同 SqlSession 的不同調(diào)用將不會(huì)共享數(shù)據(jù)蚣驼。*/
  protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
  /*當(dāng)沒有為參數(shù)提供特定的 JDBC 類型時(shí),為空值指定 JDBC 類型相艇。 
    某些驅(qū)動(dòng)需要指定列的 JDBC 類型颖杏,多數(shù)情況直接用一般類型即可,比如 NULL坛芽、VARCHAR 或 OTHER留储。*/
  protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
  //指定哪個(gè)對(duì)象的方法觸發(fā)一次延遲加載。
  protected Set<String> lazyLoadTriggerMethods = new HashSet<String>(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" }));
  //設(shè)置超時(shí)時(shí)間咙轩,它決定驅(qū)動(dòng)等待數(shù)據(jù)庫(kù)響應(yīng)的秒數(shù)获讳。

Configuration — TypeHandlerRegistry對(duì)象

類型處理器注冊(cè)對(duì)象。在構(gòu)建TypeHandlerRegistry對(duì)象的時(shí)候活喊,便將類型注冊(cè)了進(jìn)去丐膝。

類型處理器TypeHandlerRegistry簡(jiǎn)單點(diǎn)就是用于處理javaType與jdbcType之間的類型轉(zhuǎn)換用的處理器,Mybatis針對(duì)諸多Java類型與數(shù)據(jù)庫(kù)類型進(jìn)行了匹配處理钾菊。

//獲取到類型處理器
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());
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末帅矗,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子煞烫,更是在濱河造成了極大的恐慌浑此,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件滞详,死亡現(xiàn)場(chǎng)離奇詭異凛俱,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)料饥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門蒲犬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人稀火,你說我怎么就攤上這事暖哨《呐螅” “怎么了凰狞?”我有些...
    開封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵篇裁,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我赡若,道長(zhǎng)达布,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任逾冬,我火速辦了婚禮黍聂,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘身腻。我一直安慰自己产还,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開白布嘀趟。 她就那樣靜靜地躺著脐区,像睡著了一般。 火紅的嫁衣襯著肌膚如雪她按。 梳的紋絲不亂的頭發(fā)上牛隅,一...
    開封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音酌泰,去河邊找鬼媒佣。 笑死,一個(gè)胖子當(dāng)著我的面吹牛陵刹,可吹牛的內(nèi)容都是我干的默伍。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼衰琐,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼巡验!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起碘耳,我...
    開封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤显设,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后辛辨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體捕捂,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年斗搞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了指攒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡僻焚,死狀恐怖允悦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情虑啤,我是刑警寧澤隙弛,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布架馋,位于F島的核電站,受9級(jí)特大地震影響全闷,放射性物質(zhì)發(fā)生泄漏叉寂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一总珠、第九天 我趴在偏房一處隱蔽的房頂上張望屏鳍。 院中可真熱鬧,春花似錦局服、人聲如沸钓瞭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)降淮。三九已至,卻和暖如春搏讶,著一層夾襖步出監(jiān)牢的瞬間佳鳖,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工媒惕, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留系吩,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓妒蔚,卻偏偏與公主長(zhǎng)得像穿挨,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子肴盏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容