SqlSession獲取
一切從new SqlSessionFactoryBuilder().build(inputStream)
說起匙隔,build
方法最終通過xml配置文件解析生成一個Configuration
對象疑苫,注入到DefaultSqlSessionFactory
對象中返回,在這個過程中,Configuration
根據(jù)配置文件完成了以下配置:
properties
主要是把<properties>標(biāo)簽下的屬性-值或者外部properties文件中的屬性-值加載進(jìn)來缀匕,供后面替換變量
settings
加載常見的配置如defaultExecutorType
typeAliases
配置類的別名
plugins
配置插件
objectFactory
objectWrapperFactory
reflectorFactory
environments
配置dataSource和transactionManager
databaseIdProvider
typeHandlers
mappers
接下來openSession
方法默認(rèn)會根據(jù)配置的dataSource
來創(chuàng)建:
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
final Executor executor = configuration.newExecutor(tx, execType);
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
對象
Executor
在生成DefaultSqlSession
對象的過程中,傳入了一個Executor
對象乡小,該對象是真正執(zhí)行查詢的對象阔加,我們看看MyBatis中都有哪些Executor類型
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
這里的ExecutorType
就是我們在配置文件中配置的,默認(rèn)為SIMPLE
满钟,可以看出胜榔,一共有3種類型,包括SIMPLE
湃番、BATCH
夭织、REUSE
,在返回executor之前吠撮,加入了攔截鏈尊惰,我們可以利用這個來編寫插件(參考作者其他文章),對各種類型的Executor
說明可以查看官網(wǎng)或者自行閱讀源碼泥兰,這里以SIMPLE
為例說明
<hr />
到目前為止弄屡,SqlSession
對象就已經(jīng)準(zhǔn)備好了,我們可以直接用它來進(jìn)行查詢鞋诗,但是不推薦這么做膀捷,而是通過編寫Mapper
接口的方式
Mapper的動態(tài)代理
比如我們編寫了一個Mapper
接口DemoMapper
并且正確在配置文件中進(jìn)行了相關(guān)的配置,那么我們執(zhí)行下面的語句:
DemoMapper demoMapper = sqlSession.getMapper(DemoMapper.class);
首先削彬,sqlSession
將getMapper
方法委托給configuration
的getMapper
方法,最終委托給MapperRegistry
的getMapper
方法:
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
對象來生成代理對象,MyBatis會在加載配置文件時為每個Mapper生成一個MapperProxyFactory
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
可以看到融痛,MapperProxyFactory
的newInstance
方法利用JDBC的動態(tài)代理技術(shù)生成了一個T
泛型對象(最終就是我們請求的DemoMapper.class
)
MapperProxy對象
此對象實現(xiàn)了InvocationHandler壶笼,攔截方法調(diào)用
@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);
}
可以看出,當(dāng)定義method的是一個接口的時候雁刷,會生成一個MapperMethod
對象拌消,調(diào)用它的execute
方法,截取execute
方法片段:
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;
最終還是回到了sqlSession
的方法調(diào)用上面安券,因此墩崩,我們通過編寫并配置Mapper
來執(zhí)行查詢,而不是直接通過sqlSession
最終的實現(xiàn)--Executor的底層實現(xiàn)
以查詢?yōu)槔蠲悖罱K會跳轉(zhuǎn)到Executor
的query
方法鹦筹,以SimpleExecutor
為例,就是doQuery
方法
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(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
首先址貌,創(chuàng)建一個StatementHandler對象铐拐,然后預(yù)編譯SQL語句生成Statement對象徘键,最后執(zhí)行
預(yù)編譯的過程如下
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
包括了兩個過程,prepare和parameterize
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
statement = instantiateStatement(connection);
setStatementTimeout(statement, transactionTimeout);
setFetchSize(statement);
return statement;
} catch (SQLException e) {
closeStatement(statement);
throw e;
} catch (Exception e) {
closeStatement(statement);
throw new ExecutorException("Error preparing statement. Cause: " + e, e);
}
}
prepare過程主要是初始化和設(shè)置一些連接參數(shù)如超時時間遍蟋、最大返回行數(shù)
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}
parameterize就非常簡單了吹害,委托給ParameterHandler
設(shè)置每個占位符上面的參數(shù)和值,最后執(zhí)行query
:
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.<E> handleResultSets(ps);
}
到這里什么都已經(jīng)準(zhǔn)備好了,所以執(zhí)行查詢變得非常簡單虚青,就是我們編寫JDBC
時對PreparedStatement
的一般操作它呀,然后通過ResultSetHandler
來搜集返回結(jié)果
總結(jié)一下Executor的執(zhí)行流程:
- 新建
StatementHandler
對象對SQL進(jìn)行初始化和預(yù)編譯 - 利用
ParameterHandler
對象對預(yù)編譯的SQL填參數(shù) - 利用
PreparedStatement
對象執(zhí)行查詢(JDBC) - 利用
ResultSetHandler
對象搜集結(jié)果返回
對于具體的Handler有哪些類型、怎么實現(xiàn)的棒厘,讀者可以自行查閱相關(guān)源碼或文檔纵穿,到這里,整個MyBatis執(zhí)行的過程已經(jīng)很清晰了