接上一篇文章SqlSessionFactory的創(chuàng)建
http://www.reibang.com/p/eb3d06a7c77d
SqlSession的創(chuàng)建過程
既然已經(jīng)得到了SqlSessionFactory
览露,那么SqlSession
將由SqlSessionFactory
進(jìn)行創(chuàng)建截歉。
SqlSession sqlSession=sqlSessionFactory.openSession();
這樣脐区,我們就來看看這個(gè)SqlSessionFactory
的 openSession
方法是如何創(chuàng)建SqlSession對象的。根據(jù)上面的分析昭抒,這里的SqlSessionFactory
類型對象其實(shí)是一個(gè)DefaultSqlSessionFactory
對象攘蔽,因此宋下,需要到DefaultSqlSessionFactory
類中去看openSession
方法。
// DefaultSqlSessionFactory 類
@Override
public SqlSession openSession() {
return openSessionFromDataSource(
configuration.getDefaultExecutorType(), null, false);
}
/**
* 這里對參數(shù)類型進(jìn)行說明
* ExecutorType 指定Executor的類型篡腌,分為三種:SIMPLE, REUSE, BATCH
* TransactionIsolationLevel 指定事務(wù)隔離級別
* 使用null,則表示使用數(shù)據(jù)庫默認(rèn)的事務(wù)隔離界別
* autoCommit 是否自動(dòng)提交
*/
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
// 獲取配置中的環(huán)境信息,包括了數(shù)據(jù)源信息勾效、事務(wù)等
final Environment environment = configuration.getEnvironment();
// 創(chuàng)建事務(wù)工廠
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
// 創(chuàng)建事務(wù)哀蘑,配置事務(wù)屬性
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 創(chuàng)建Executor,即執(zhí)行器
// 它是真正用來Java和數(shù)據(jù)庫交互操作的類葵第,后面會(huì)展開說绘迁。
final Executor executor = configuration.newExecutor(tx, execType);
// 創(chuàng)建DefaultSqlSession對象返回,因?yàn)镾qlSession是一個(gè)接口
// 可以類比DefaultSqlSessionFactory
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
這樣我們就得到了DefaultSqlSession
(SqlSession)卒密∽禾ǎ可以看到基本覆蓋數(shù)據(jù)庫的各種操作,增刪查改哮奇,以及簡單的事務(wù)的操作膛腐。
接下來就要看看它的執(zhí)行過程。
SqlSession
的執(zhí)行過程
獲取到了SqlSession
之后鼎俘,則需要執(zhí)行下面的語句
CountryMapper countryMapper=
sqlSession.getMapper(CountryMapper.class);
因此我們要看看getMapper這個(gè)方法干了什么哲身。上面的分析知道,這里的sqlSession其實(shí)是DefaultSqlSession
對象贸伐,因此需要在DefaultSqlSession
中去查看這個(gè)方法勘天。
// DefaultSqlSession 類
@Override
public <T> T getMapper(Class<T> type) {
return configuration.<T>getMapper(type, this);
}
這里的configuration
是Configuration
類的實(shí)例,在SqlSessionFactory
中創(chuàng)建并完成所有配置的解析后捉邢,初始化DefaultSqlSession
時(shí)脯丝,SqlSessionFactory
將配置作為屬性傳給DefaultSqlSession
,因此前面解析的所有配置伏伐,都能在這里查到宠进。
因此我們繼續(xù)往下看,這里就要定位到Configuration
類的getMapper
方法了藐翎。
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
// mapperRegistry 是一個(gè)mapper配置的容器材蹬,前面有提到
// 配置路徑下所有掃描到的mapper在初始化完成Configuration以后,都會(huì)加載進(jìn)來
// 每一個(gè)mapper都被存儲(chǔ)在了MapperRegistry的knownMappers中了
// 在初始化配置的時(shí)候執(zhí)行addMapper吝镣,在獲取Mapper的時(shí)候執(zhí)行g(shù)etMapper
return mapperRegistry.getMapper(type, sqlSession);
}
因此堤器,我們就要來看看MapperRegistry
的getMapper
方法
// MapperRegistry的getMapper方法
@SuppressWarnings("unchecked")
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
// 從knownMappers集合中獲取mapper,創(chuàng)建MapperProxyFactory
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
// 獲取代理對象,并返回
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
看到了MapperProxyFactory
很明顯這是一個(gè)工廠類赤惊,所以肯定會(huì)有MapperProxy
這么一個(gè)類吼旧,而看到Proxy
,這里肯定用到了代理未舟,也肯定就是動(dòng)態(tài)代理了圈暗。
我們來看看獲取代理對象的方法 newInstance
// MapperProxyFactory 類
...
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
// MapperProxy實(shí)現(xiàn)了InvocationHandler掂为,擴(kuò)展了invoke方法,維護(hù)代理邏輯员串。
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
...
這里我們可以看到動(dòng)態(tài)代理對接口的綁定勇哗,它的作用就是生成動(dòng)態(tài)代理對象。這里最終返回了CountryMapper
接口的代理對象寸齐。
而代理對象則被放到了MapperProxy
中欲诺。通過idea
打斷點(diǎn),來查看CountryMapper
的詳細(xì)信息渺鹦,我們也可以看到這是一個(gè)MapperProxy
對象扰法。
因此,在執(zhí)行countryMapper.selectAll()
方法時(shí)毅厚,便會(huì)進(jìn)入到MapperProxy
的invoke方法中來塞颁。
我們來看一下MapperProxy
的部分代碼。
// MapperProxy類
...
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// 判斷是否是一個(gè)類
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);
}
// 顯然吸耿,我們這里是一個(gè)接口祠锣,則執(zhí)行下面的流程
// 生成MapperMethod對象,通過cachedMapperMethod初始化
final MapperMethod mapperMethod = cachedMapperMethod(method);
// 執(zhí)行execute方法咽安, 把sqlSession和當(dāng)前參數(shù)傳遞進(jìn)去
return mapperMethod.execute(sqlSession, args);
}
...
接著來看看execute方法
// MapperMethod 類的方法
// MapperMethod采用命令模式運(yùn)行伴网,根據(jù)上下文跳轉(zhuǎn)
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
...
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
// 主要看這個(gè)方法
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;
...
}
}
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
List<E> result;
// 將Java參數(shù)轉(zhuǎn)換為Sql命令行參數(shù)
Object param = method.convertArgsToSqlCommandParam(args);
// 是否需要分頁
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
// 通過SqlSession對象執(zhí)行查詢,帶分頁
// command.getName() 獲取Mapper接口當(dāng)前執(zhí)行方法selectAll的全路徑名
result = sqlSession.<E>selectList(command.getName(), param, rowBounds);
} else {
// 通過SqlSession對象執(zhí)行查詢妆棒,不帶分頁
result = sqlSession.<E>selectList(command.getName(), param);
}
// 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;
}
至此已經(jīng)基本可以明白了澡腾,Mybatis為什么只用Mapper接口就可以運(yùn)行SQL,因?yàn)橛成淦鞯腦ML文件的命名空間對應(yīng)的便是這個(gè)接口的全路徑募逞,它能根據(jù)全路徑和方法名綁定起來蛋铆,通過動(dòng)態(tài)代理技術(shù)馋评,讓這個(gè)接口跑起來放接。然后采用命令模式,根據(jù)上下文跳轉(zhuǎn)留特,最終還是使用SqlSession接口的方法使得它能夠進(jìn)行查詢纠脾。
接著我們就來看看selectList
的源碼。
result = sqlSession.<E>selectList(command.getName(), param);
注意這里的sqlSession蜕青,其實(shí)是DefaultSqlSession
的對象苟蹈,因此要去看DefaultSqlSession
的selectList方法。
@Override
public <E> List<E> selectList(String statement, Object parameter) {
// 分頁參數(shù)選擇默認(rèn)右核,表示不分頁
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
// 從Configuration配置中獲取MappedStatement對象
MappedStatement ms = configuration.getMappedStatement(statement);
// 使用executor進(jìn)行查詢
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();
}
}
接著就進(jìn)入了Executor
查詢之后慧脱,將結(jié)果返回。那么Executor
是如何進(jìn)行查詢的呢贺喝?
下一篇文章再見菱鸥!