您好,登錄后才能下訂單哦!
上一篇介紹了反射和動態(tài)代理基礎,主要是為本篇文章做個鋪墊,反射使配置和靈活性大大提高,可以給很多配置設置參數(shù),動態(tài)代理可以在運行時創(chuàng)建代理對象,做一些特殊的處理。
本篇會介紹MyBatis解析和運行原理,下一篇介紹插件及應用,目的是更好地編寫插件,通過本篇的介紹,你會了解到:
SqlSessionFactory和SqlSession是MyBatis的核心組件,在文章 JDBC和MyBatis介紹 中有詳細說明。
構(gòu)建主要分為2步:
說白了,就是將我們的所有配置解析為Configuration對象,在整個生命周期內(nèi),可以通過該對象獲取需要的配置。
由于插件需要頻繁訪問映射器的內(nèi)部組成,會重點這部分,了解這塊配置抽象出來的對象:
它保存映射器的一個節(jié)點(select|insert|delete|update),包括配置的SQL,SQL的id、緩存信息、resultMap、parameterType、resultType等重要配置內(nèi)容。
它涉及的對象比較多,一般不去修改它。
它是MappedStatement的一個屬性,主要作用是根據(jù)參數(shù)和其他規(guī)則組裝SQL,也是很復雜的,一般也不用修改它。
對于參數(shù)和SQL,主要反映在BoundSql類對象上,在插件中,通過它獲取到當前運行的SQL和參數(shù)以及參數(shù)規(guī)則,作出適當?shù)男薷?,滿足特殊的要求。
BoundSql提供3個主要的屬性:parameterObject、parameterMappings和sql,下面分別來介紹。
parameterObject為參數(shù)本身,可以傳遞簡單對象、POJO、Map或@Param注解的參數(shù):
parameterMappings,它是一個List,元素是ParameterMapping對象,這個對象會描繪sql中的參數(shù)引用,包括名稱、表達式、javaType、jdbcType、typeHandler等信息。
sql,是寫在映射器里面的一條sql。
有了Configuration對象,構(gòu)建SqlSessionFactory就簡單了:
sqlSessionFactory = new SqlSessionFactoryBuilder().bulid(inputStream);
Mapper映射是通過動態(tài)代理來實現(xiàn)的,使用JDK動態(tài)代理返回一個代理對象,供調(diào)用者訪問。
首先看看實現(xiàn)InvocationHandler接口的類,它是執(zhí)行本代理方法的關鍵,可以看到,Mapper是一個接口,會生成MapperMethod對象,調(diào)用execute方法。
public class MapperProxy<T> implements InvocationHandler, Serializable {
.....
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
}
看下面的代碼,MapperMethod采用命令模式,根據(jù)不同的sql操作,做不同的處理。
public class MapperMethod {
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
......
}
}
}
最后看下,生成代理類的方法,就是使用JDK動態(tài)代理Proxy來創(chuàng)建的。
public class MapperProxyFactory<T> {
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
}
總結(jié)下映射器的調(diào)用過程,返回的Mapper對象是代理對象,當調(diào)用它的某個方法時,其實是調(diào)用MapperProxy#invoke方法,而映射器的XML文件的命名空間對應的就是這個接口的全路徑,會根據(jù)全路徑和方法名,便能夠綁定起來,定位到sql,最后會使用SqlSession接口的方法使它能夠執(zhí)行查詢。
通過上面的分析,映射器就是一個動態(tài)代理對象,進入到了MapperMethod的execute方法,它經(jīng)過簡單的判斷就進入了SqlSession的刪除、更新、插入、選擇等方法,這些方法如何執(zhí)行是下面要介紹的內(nèi)容。
Mapper執(zhí)行的過程是通過Executor、StatementHandler、ParameterHandler和ResultHandler來完成數(shù)據(jù)庫操作和結(jié)果返回的,理解他們是編寫插件的關鍵:
在MyBatis中存在三種執(zhí)行器:
以SimpleExecutor為例,說明執(zhí)行過程
public class SimpleExecutor extends BaseExecutor {
/**
* 執(zhí)行查詢操作
*/
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
/**
* 初始化StatementHandler
*/
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection);
handler.parameterize(stmt);
return stmt;
}
/**
* 執(zhí)行查詢
*/
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
String sql = boundSql.getSql();
statement.execute(sql);
return resultSetHandler.<E>handleResultSets(statement);
}
}
可以看到最后會委托給StatementHandler會話器進行處理,它是一個接口,實際創(chuàng)建的是RoutingStatementHandler對象,但它不是真實的服務對象,它是通過適配器模式找到對應的StatementHandler執(zhí)行的。在MyBatis中,StatementHandler和Executor一樣分為三種:SimpleStatementHandler、PreparedStatementHandler、CallableStatementHandler。
Executor會先調(diào)用StatementHandler的prepare方法預編譯SQL語句,同時設置一些基本運行的參數(shù)。然后調(diào)用parameterize()方法啟用ParameterHandler設置參數(shù),完成預編譯,跟著執(zhí)行查詢,用ResultHandler封裝結(jié)果返回給調(diào)用者。
參數(shù)處理器和結(jié)果處理器比較簡單,就不在此介紹了。
下一篇會介紹插件及其應用,主要是在sql執(zhí)行的過程中,在四大對象的基礎上進行擴展。
歡迎掃描下方二維碼,關注我的個人微信公眾號 ~
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。