前言
在上一篇文章中我們分析了StatementHandler的源碼,本文我們會(huì)分析Mybatis中四大接口之一的ParameterHandler的源碼部分
源碼
ParameterHandler.java
參數(shù)處理器符糊,用來(lái)給PreparedStatement設(shè)置參數(shù)的
接口定義
public interface ParameterHandler {
// 獲取當(dāng)前執(zhí)行實(shí)際傳入的參數(shù)
Object getParameterObject();
// 給PreparedStatement設(shè)置參數(shù)
void setParameters(PreparedStatement ps)
throws SQLException;
}
接口定義好了凫海,活就需要有人來(lái)做了,下面看看ParameterHandler接口有哪些實(shí)現(xiàn)
DefaultParameterHandler.java
Mybatis僅僅提供了一個(gè)實(shí)現(xiàn)類男娄,我們直接看其源碼
看其實(shí)現(xiàn)類的源碼之前行贪,我們先設(shè)想一下,如果是讓你自己來(lái)實(shí)現(xiàn)這個(gè)接口模闲,你的實(shí)現(xiàn)中應(yīng)該有哪些屬性呢? 確定實(shí)現(xiàn)有哪些屬性之前我們先仔細(xì)分析一下接口中定義的方法建瘫,接口方法只有兩個(gè),一個(gè)獲取當(dāng)前實(shí)際執(zhí)行的參數(shù)對(duì)象尸折,注意是這種形式的({{"phone", "15800000000"}, {"param1", "15800000000"}}
)
所以實(shí)現(xiàn)中必然需要有一個(gè)屬性暖混,類似下面所示
// 當(dāng)前實(shí)際執(zhí)行前的參數(shù)對(duì)象
private final Object parameterObject;
除此之外的另一個(gè)接口方法是為PreparedStatement
對(duì)象設(shè)置動(dòng)態(tài)運(yùn)行參數(shù),既然是設(shè)置參數(shù)翁授,我就需要拿到整個(gè)MappedStatement對(duì)象以及BoundSql對(duì)象,通過(guò)BoundSql我就可以得到帶有?號(hào)的sql語(yǔ)句晾咪,當(dāng)前傳遞的參數(shù)以及整個(gè)sql語(yǔ)句的參數(shù)映射關(guān)系List<ParameterMapping>
收擦,(這個(gè)映射關(guān)系尤為重要,可以說(shuō)是ParameterHandler實(shí)現(xiàn)的關(guān)鍵)谍倦,通過(guò)參數(shù)映射關(guān)系我就可以準(zhǔn)確的找到哪個(gè)參數(shù)對(duì)應(yīng)
哪個(gè)TypeHandler
塞赂,以及該參數(shù)是入?yún)?/code>還是
出參
類型,其javaType
是什么昼蛀,jdbcType
是什么等等信息
ok宴猾,分析到這里基本上差不多了,我們還是直接看源碼吧
public class DefaultParameterHandler implements ParameterHandler {
// 屬性
// 持有typeHandler注冊(cè)器
private final TypeHandlerRegistry typeHandlerRegistry;
// 持有MappedStatement實(shí)例叼旋,這是一個(gè)靜態(tài)的xml的一個(gè)數(shù)據(jù)庫(kù)操作節(jié)點(diǎn)的靜態(tài)信息而已
private final MappedStatement mappedStatement;
// 持有當(dāng)前操作傳入的實(shí)際參數(shù)
private final Object parameterObject;
// 動(dòng)態(tài)語(yǔ)言被執(zhí)行后的結(jié)果sql
private final BoundSql boundSql;
private final Configuration configuration;
// 構(gòu)造函數(shù)
public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
this.mappedStatement = mappedStatement;
this.configuration = mappedStatement.getConfiguration();
this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
this.parameterObject = parameterObject;
this.boundSql = boundSql;
}
// 實(shí)現(xiàn)方法
@Override
public Object getParameterObject() {
return parameterObject;
}
@Override
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
// 1. 獲取boundSql中的參數(shù)映射信息列表
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
// 1.1. 遍歷參數(shù)映射列表仇哆,這個(gè)列表信息就是我們xml文件中定義的某個(gè)查詢語(yǔ)句的所有參數(shù)映射信息,注意這個(gè)List中的參數(shù)映射元素的順序是和真實(shí)xml中sql的參數(shù)順序?qū)?yīng)的
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
// 1.2. 只有入?yún)㈩愋筒艜?huì)設(shè)置PreparedStatement
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
// 取出參數(shù)名夫植,這里比如說(shuō)是'phone'
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
// 1.3. 這一步的工作就是從當(dāng)前實(shí)際傳入的參數(shù)中獲取到指定key('phone')的value值讹剔,比如是'15800000000'
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
// 2. 獲取該參數(shù)對(duì)應(yīng)的typeHandler
TypeHandler typeHandler = parameterMapping.getTypeHandler();
// 2.1. 獲取該參數(shù)對(duì)應(yīng)的jdbcType
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
// 3. 重點(diǎn)是調(diào)用每個(gè)參數(shù)對(duì)應(yīng)的typeHandler的setParameter方法為該ps設(shè)置正確的參數(shù)值
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
} catch (SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}
}
這里截取一個(gè)運(yùn)行時(shí)的debug信息看一下就知道了
至此我們可以知道,每一次的調(diào)用查詢(數(shù)據(jù)庫(kù)查詢详民,不走M(jìn)ybatis緩存)延欠,Executor在執(zhí)行doQuery的時(shí)候都會(huì)創(chuàng)建一個(gè)StatementHandler
實(shí)例,每個(gè)StatementHandler
在實(shí)例化的時(shí)候沈跨,都會(huì)創(chuàng)建并持有兩個(gè)處理器即ParameterHandler
和ResultSetHandler
這樣statementHandler
就可以利用ParameterHandler
完成預(yù)處理語(yǔ)句的參數(shù)化設(shè)置
由捎,以及結(jié)果查詢出來(lái)以后再利用ResultSetHandler
處理結(jié)果集
這里我們?cè)俅钨N一下BaseStatementHandler的構(gòu)造函數(shù)部分源碼
// BaseStatementHandler.java
// ...
protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
// 省略其他屬性設(shè)置...
// 1. parameterHandler
this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
// 2. resultSetHandler
this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
}
我們看下parameterHandler的創(chuàng)建過(guò)程,可以看到是利用了configuration
// Configuration.java
// ...
// new一個(gè)參數(shù)化處理器
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
// 1. 內(nèi)部就是調(diào)用了DefaultParameterHandler的構(gòu)造方法饿凛,new了一個(gè)實(shí)例返回
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
// 2. 看到這里狞玛,又是Mybatis的插件软驰,提供給開發(fā)人員攔截ParameterHandler的調(diào)用邏輯
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}
代碼分析至此,我們簡(jiǎn)單總結(jié)一下
當(dāng)真實(shí)的數(shù)據(jù)庫(kù)查詢被執(zhí)行時(shí)为居,
SimpleExecutor執(zhí)行器
會(huì)通過(guò)configuration
對(duì)象new
出一個(gè)StatementHandler
出來(lái)碌宴,后續(xù)的所有操作都是利用這個(gè)statementHandler來(lái)完成,這個(gè)handler實(shí)際上是返回了經(jīng)過(guò)開發(fā)人員自定義插件的代理類
蒙畴,可以基于此處自定義插件當(dāng)statementHandler實(shí)例有了以后贰镣,我們需要?jiǎng)?chuàng)建出真實(shí)的JDBC
Statement
實(shí)例,此處調(diào)用了statementHandler的prepare方法
(此處可自定義插件攔截哦)實(shí)現(xiàn)膳凝,根據(jù)handler的類型不同則會(huì)創(chuàng)建不同的JDBCStatement
實(shí)例返回碑隆,這里簡(jiǎn)單的提一下,如果是ReuseExecutor
不會(huì)為同一個(gè)BoundSql中的sql語(yǔ)句
創(chuàng)建多個(gè)prepareStatement
返回蹬音,而是在內(nèi)部維護(hù)一個(gè)Map<String, Statement>
來(lái)重用Statement實(shí)例當(dāng)statement實(shí)例返回時(shí)上煤,就會(huì)進(jìn)行
參數(shù)化設(shè)置
(如果是一個(gè)Statement實(shí)例而非PrepareStatement實(shí)例,參數(shù)化設(shè)置方法的內(nèi)部就不作任何處理)著淆,調(diào)用statementHandler的parameterize
方法完成劫狠,由于我們每個(gè)statementHandler都持有兩個(gè)處理器即ParameterHandler
和ResultSetHandler
,這個(gè)時(shí)候就是ParameterHandler
上場(chǎng)的時(shí)候了永部,PrepareStatementHandler類型的handler在參數(shù)化方法內(nèi)部就是委托給了ParameterHandler
独泞,調(diào)用了其setParameters
方法進(jìn)行參數(shù)化設(shè)置,而setParameters
方法的核心實(shí)現(xiàn)就是拿到當(dāng)前調(diào)用的boundSql實(shí)例中的parameterMappings
集合和parameterObject
實(shí)例苔埋,通過(guò)遍歷
這個(gè)集合懦砂,對(duì)所有入?yún)㈩愋?ParameterMode.IN)
的參數(shù)進(jìn)行逐個(gè)獲取其參數(shù)對(duì)應(yīng)
的TypeHandler
,再利用TypeHandler
的setParameter方法
對(duì)當(dāng)前的preparedStatement
實(shí)例進(jìn)行參數(shù)化設(shè)置组橄,而設(shè)置需要的參數(shù)值就是從parameterObject
中獲取的當(dāng)參數(shù)設(shè)置完成以后荞膘,statementHandler又拿到了調(diào)用權(quán),開始調(diào)用
query
/update(Statement)
方法執(zhí)行數(shù)據(jù)庫(kù)操作玉工,不同的handler則有不同的實(shí)現(xiàn)羽资,比如SimpleStatementHandler
就是調(diào)用的Statement.execute(sql)
執(zhí)行,而PreparedStatementHandler
則是直接ps.execute()
執(zhí)行即可數(shù)據(jù)庫(kù)執(zhí)行完成后瓮栗,就拿到了
結(jié)果集
削罩,這個(gè)時(shí)候就是ResultSetHandler
發(fā)揮的時(shí)候了,下文我們會(huì)繼續(xù)分析ResultSetHandler的源碼