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中的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];
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—解析后的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是一個(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ì)象的方法又不相同渐夸。
注意:如果真實(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ù)涡相。
需要注意的是哲泊,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);
}
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完成參數(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());
}