前言
本文銜接上文(Mapper接口篇)慰技,繼續(xù)分析Mapper相關(guān)的源碼
進(jìn)入源碼
上文中我們分析到了MapperMethod的execute方法
MapperMethod.java
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;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
// 根據(jù)上文示例糟红,代碼會(huì)執(zhí)行到這里
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
根據(jù)上文示例陈肛,我們的代碼會(huì)執(zhí)行到executeForMany(sqlSession, args);這個(gè)分支句旱,下面分析這個(gè)方法做了什么工作
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
List<E> result;
// 1. 首先將傳入的Object[] args轉(zhuǎn)為上文分析的合適的參數(shù)
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
// 1.1. 該調(diào)用方法有行限制參數(shù)RowBounds
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.<E>selectList(command.getName(), param, rowBounds);
} else {
// 1.2. 無行限制參數(shù)RowBounds
result = sqlSession.<E>selectList(command.getName(), param);
}
// 對于返回值為Collections和Array的支持,因?yàn)镾qlSession接口定義的返回值全部都是List<T>,如果我們的Mapper接口中定義了Array類型的返回值夹厌,就需要進(jìn)行轉(zhuǎn)換
// issue #510 Collections & arrays support
if (!method.getReturnType().isAssignableFrom(result.getClass())) {
if (method.getReturnType().isArray()) {
return convertToArray(result);
} else {
return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
}
}
return result;
}
method.convertArgsToSqlCommandParam(args);
這里在真正調(diào)用SqlSession的相關(guān)方法前會(huì)執(zhí)行MethodSignature的convertArgsToSqlCommandParam(Object[] args)
方法
public Object convertArgsToSqlCommandParam(Object[] args) {
return paramNameResolver.getNamedParams(args);
}
就是調(diào)用了內(nèi)部持有的paramNameResolver
對象的getNamedParams
方法或南,將從代理Mapper對象方法調(diào)用拿到的Object[] args
參數(shù)轉(zhuǎn)為合適的名稱參數(shù)對象
以便接下來的sqlSession相關(guān)方法執(zhí)行使用
接下來,我們將目光轉(zhuǎn)向Mybatis中大名鼎鼎的Sqlsession
SqlSession.java
Mybatis暴露的頂層Api接口,用于操作Sql的執(zhí)行冀瓦,以屏蔽掉底層JDBC操作數(shù)據(jù)庫的繁瑣,讓我們可以直接調(diào)用其申明的各種方便的方法如,查詢/新增/更新/刪除/提交事務(wù)/回滾事務(wù)/獲取Mapper代理對象等
下面看下SqlSession
的接口定義
public interface SqlSession extends Closeable {
// 查詢單個(gè)結(jié)果
<T> T selectOne(String statement);
<T> T selectOne(String statement, Object parameter);
// 查詢List<E>結(jié)果撑毛,可指定RowBounds參數(shù)
<E> List<E> selectList(String statement);
<E> List<E> selectList(String statement, Object parameter);
<E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds);
// 返回Map的查詢方式
<K, V> Map<K, V> selectMap(String statement, String mapKey);
<K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey);
<K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds);
// 返回懶處理迭代器Cursor游標(biāo)的查詢雌续,注意這種方式的查詢非常適合與數(shù)百萬數(shù)據(jù)項(xiàng)的查詢(不會(huì)直接加載進(jìn)內(nèi)存中)
<T> Cursor<T> selectCursor(String statement);
<T> Cursor<T> selectCursor(String statement, Object parameter);
<T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds);
// 指定resultHandler參數(shù)的查詢
void select(String statement, Object parameter, ResultHandler handler);
void select(String statement, ResultHandler handler);
void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler);
// insert相關(guān)
int insert(String statement);
int insert(String statement, Object parameter);
// update相關(guān)
int update(String statement);
int update(String statement, Object parameter);
// delete相關(guān)
int delete(String statement);
int delete(String statement, Object parameter);
// 事務(wù)相關(guān),force參數(shù)代表是否強(qiáng)制提交/回滾事務(wù)
void commit();
void commit(boolean force);
void rollback();
void rollback(boolean force);
// 檢索當(dāng)前Mybatis配置對象
Configuration getConfiguration();
// 檢索一個(gè)綁定到當(dāng)前SqlSession對象的type類型的Mapper代理對象
<T> T getMapper(Class<T> type);
// 檢錯(cuò)內(nèi)部持有的數(shù)據(jù)庫連接對象
Connection getConnection();
}
接口終歸是接口胯杭,活還是得有人來干
驯杜,下面看看SqlSession的實(shí)現(xiàn)類有哪些
DefaultSqlSession.java
Mybatis 默認(rèn)的SqlSession實(shí)現(xiàn)類,實(shí)現(xiàn)了接口中定義的所有功能
接口定義
public class DefaultSqlSession implements SqlSession {...}
可以看到這里DefaultSqlSession僅實(shí)現(xiàn)了SqlSession接口
內(nèi)部屬性定義
// 重要屬性做个,持有Mybatis Configuration實(shí)例
private final Configuration configuration;
// 持有執(zhí)行器Executor鸽心,后面會(huì)詳細(xì)分析
private final Executor executor;
// 是否自動(dòng)提交事務(wù)
private final boolean autoCommit;
// 是否已過期,執(zhí)行update操作就會(huì)置為true
private boolean dirty;
// 當(dāng)查詢數(shù)以百萬計(jì)的數(shù)據(jù)時(shí)用到
private List<Cursor<?>> cursorList;
每個(gè)SqlSession實(shí)例內(nèi)部都持有一個(gè)Mybatis的配置實(shí)例以及一個(gè)執(zhí)行器Executor實(shí)例,且在其構(gòu)造函數(shù)中就需要初始化這兩個(gè)屬性了
構(gòu)造函數(shù)
public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
this.configuration = configuration;
this.executor = executor;
this.dirty = false;
this.autoCommit = autoCommit;
}
public DefaultSqlSession(Configuration configuration, Executor executor) {
this(configuration, executor, false);
}
接下來我們繼續(xù)上面的查詢例子sqlSession.<E>selectList(command.getName(), param);
這里的command.getName的值是"com.wilson.mapper.UserOrderMapper.completedOrdersByUserPhone"
這個(gè)字符串居暖,而param則是一個(gè)Map對象顽频,其值為{{"phone", "15800000000"}, {"param1", "15800000000"}}
ok,接下來我們直接看DefaultSqlSession的selectList方法做了什么
@Override
public <E> List<E> selectList(String statement, Object parameter) {
// 內(nèi)部調(diào)用重載的帶有RowBounds參數(shù)的方法膝但,傳入默認(rèn)的RowBounds冲九,即offset為0,limit為Integer的最大值
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
@Override
public <E> List<E> selectList(String statement, Object
parameter, RowBounds rowBounds) {
try {
// 1.
MappedStatement ms = configuration.getMappedStatement(statement);
// 2.
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
重點(diǎn):
- 首先從
configuration
實(shí)例中獲取到MappedStatement
實(shí)例 - 調(diào)用執(zhí)行器
executor
的query
方法跟束,并傳入1. 中得到的MappedStatement實(shí)例作為參數(shù)
以上兩步我們分開分析莺奸,首先看第一步如何從配置對象configuration中獲取到MappedStatement實(shí)例
根據(jù)statementId獲取MappedStatement實(shí)例
// Configuration.java
protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
public MappedStatement getMappedStatement(String id) {
// 根據(jù)傳入的statementId獲取
return this.getMappedStatement(id, true);
}
public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) {
if (validateIncompleteStatements) {
buildAllStatements();
}
// 就是從mappedStatements取出已經(jīng)存入的MappedStatement實(shí)例
return mappedStatements.get(id);
}
總結(jié)
至此本文內(nèi)容分析結(jié)束,SqlSession真正的SQL查詢
交給了Executor執(zhí)行器
冀宴,前文不是說過SqlSession是Mybatis提供的頂層API嘛灭贷,通過SqlSession就可以操作數(shù)據(jù)庫的各種SQL操作了,這里怎么又出來一個(gè)執(zhí)行器呢略贮?實(shí)際上甚疟,Mybatis的SqlSession
非常聰明,將各種SQL操作的臟活逃延、累活
都委托
給了Executor執(zhí)行器
干览妖,自己則坐享其成
...
下文我們將分析Mybatis的Executor篇