mybatis源碼分析
準備工作
我們首先導入mybatis源碼,首先以傳統方式執行一個查詢,通過打斷點來分析mybatis的整個查詢執行流程。
@Test
public void query() throws IOException {
//1.Resources工具類,配置文件的加載,把配置文件加載成字節輸入流
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
//2.解析了配置文件,並創建了sqlSessionFactory工廠
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//3.生產sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();// 默認開啓一個事務,但是該事務不會自動提交
//在進行增刪改操作時,要手動提交事務
//4.sqlSession調用方法:查詢所有selectList 查詢單個:selectOne 添加:insert 修改:update 刪除:delete
List<User> users = sqlSession.selectList("com.lagou.dao.IUserDao.findAll");
for (User user : users) {
System.out.println(user);
}
//IUserDao userDao = sqlSession.getMapper(IUserDao.class);
//List<User> all = userDao.findAll();
//System.out.println(all);
sqlSession.close();
}
執行過程:
1.讀取配置信息
public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {
InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader);
if (in == null) {
throw new IOException("Could not find resource " + resource);
}
return in;
}
首先是讀取了配置文件,加載成輸入流將其返回
2.構建一個SqlSessionFactory
// 2.調用的重載方法
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
// 創建 XMLConfigBuilder, XMLConfigBuilder是專門解析mybatis的配置文件的類
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
// 執行 XML 解析
// 創建 DefaultSqlSessionFactory 對象
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
解析了配置文件,並創建了sqlSessionFactory工廠,在build方法中首先會解析創建出來一個Configuration對象出來,mybatis在初始化過程中,將配置信息全部加載到內存中,org.apache.ibatis.session.Configuration類來維護。Configuration的結構也與其xml的配置文件標籤基本相同。
/**
* 解析 XML 成 Configuration 對象。
*
* @return Configuration 對象
*/
public Configuration parse() {
// 若已解析,拋出 BuilderException 異常
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
// 標記已解析
parsed = true;
///parser是XPathParser解析器對象,讀取節點內數據,<configuration>是MyBatis配置文件中的頂層標籤
// 解析 XML configuration 節點
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
/**
* 解析 XML
*
* 具體 MyBatis 有哪些 XML 標籤,參見 《XML 映射配置文件》http://www.mybatis.org/mybatis-3/zh/configuration.html
*
* @param root 根節點
*/
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
// 解析 <properties /> 標籤
propertiesElement(root.evalNode("properties"));
// 解析 <settings /> 標籤
Properties settings = settingsAsProperties(root.evalNode("settings"));
// 加載自定義的 VFS 實現類
loadCustomVfs(settings);
// 解析 <typeAliases /> 標籤
typeAliasesElement(root.evalNode("typeAliases"));
// 解析 <plugins /> 標籤
pluginElement(root.evalNode("plugins"));
// 解析 <objectFactory /> 標籤
objectFactoryElement(root.evalNode("objectFactory"));
// 解析 <objectWrapperFactory /> 標籤
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
// 解析 <reflectorFactory /> 標籤
reflectorFactoryElement(root.evalNode("reflectorFactory"));
// 賦值 <settings /> 到 Configuration 屬性
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
// 解析 <environments /> 標籤
environmentsElement(root.evalNode("environments"));
// 解析 <databaseIdProvider /> 標籤
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
// 解析 <typeHandlers /> 標籤
typeHandlerElement(root.evalNode("typeHandlers"));
// 解析 <mappers /> 標籤
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
/**
* MappedStatement 映射
*
* KEY:`${namespace}.${id}`
*/
protected final Map<String, MappedStatement> mappedStatements = new StrictMap<>("Mapped Statements collection");
在parse方法中,會完成對configuration
的解析,需要注意的是在configuration
對象中有着一個存儲類型爲map的mappedStatements
,這個就是對應mapper當中的每一個select|insert|delete|update
的節點,每一個節點都用MappedStatement
這個對象所維護(其中維護了sqlText,resultType,parameterTyep等屬性),然後將這些節點對象都存在一個map當中。
/**
* 創建 DefaultSqlSessionFactory 對象
*
* @param config Configuration 對象
* @return DefaultSqlSessionFactory 對象
*/
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config); //構建者設計模式
}
然後完成一個sqlSessionFactory對象的包裝返回,這裏採用了構建者設計模式。
3.打開SqlSession
//6. 進入openSession方法
@Override
public SqlSession openSession() {
//getDefaultExecutorType()傳遞的是SimpleExecutor
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
在openSession
中有三個參數,
- 第一個是一個執行器
Executor
,在configuration中默認爲simple
的, - 第二個參數
level
爲數據庫事務的隔離級別, - 第三個參數
autoCommit
爲是否提交事務
//7. 進入openSessionFromDataSource。
//ExecutorType 爲Executor的類型,TransactionIsolationLevel爲事務隔離級別,autoCommit是否開啓事務
//openSession的多個重載方法可以指定獲得的SeqSession的Executor類型和事務的處理
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
// 獲得 Environment 對象
final Environment environment = configuration.getEnvironment();
// 創建 Transaction 對象
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 創建 Executor 對象
final Executor executor = configuration.newExecutor(tx, execType);
// 創建 DefaultSqlSession 對象
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
// 如果發生異常,則關閉 Transaction 對象
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
獲取數據環境對象,事務對象,然後創建了一個executor執行器,最後返回一個sqlSession
4.執行sql獲取結果
//進入selectList方法,多個重載方法
@Override
public <E> List<E> selectList(String statement) {
return this.selectList(statement, null);
}
@Override
public <E> List<E> selectList(String statement, Object parameter) {
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
// 獲得 MappedStatement 對象
MappedStatement ms = configuration.getMappedStatement(statement);
// 執行查詢
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();
}
}
調用selectList方法進行查詢,這裏的參數爲statementId和sql執行的參數,首先通過statement獲取到MappedStatement對象,這個對象就是在之前創建Configuration時中包含的對象,其中包含sql,返回結果類型,參數類型等相關信息。
然後通過executor執行器去執行獲取結果。
Executor執行器
BaseExecutor:
//此方法在SimpleExecutor的父類BaseExecutor中實現
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//根據傳入的參數動態獲得SQL語句,最後返回用BoundSql對象表示
BoundSql boundSql = ms.getBoundSql(parameter);
//爲本次查詢創建緩存的Key
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
// 查詢
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
@SuppressWarnings("unchecked")
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
// 已經關閉,則拋出 ExecutorException 異常
if (closed) {
throw new ExecutorException("Executor was closed.");
}
// 清空本地緩存,如果 queryStack 爲零,並且要求清空本地緩存。
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
// queryStack + 1
queryStack++;
// 從一級緩存中,獲取查詢結果
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
// 獲取到,則進行處理
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
// 獲得不到,則從數據庫中查詢
} else {
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
// queryStack - 1
queryStack--;
}
if (queryStack == 0) {
// 執行延遲加載
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
// 清空 deferredLoads
deferredLoads.clear();
// 如果緩存級別是 LocalCacheScope.STATEMENT ,則進行清理
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
// 從數據庫中讀取操作
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
// 在緩存中,添加佔位對象。此處的佔位符,和延遲加載有關,可見 `DeferredLoad#canLoad()` 方法
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
// 執行讀操作
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
// 從緩存中,移除佔位對象
localCache.removeObject(key);
}
// 添加到緩存中
localCache.putObject(key, list);
// 暫時忽略,存儲過程相關
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
在第一個query()
中,首先獲取BoundSql ,這個類就是對sql進行一個封裝,然後創建一個緩存的key,最後執行第二個查詢方法query()
。
在第二個query()
中,先用緩存key去從緩存中獲取結果對象,如果沒有則從數據庫查詢,執行queryFromDatabase()
。
在queryFromDatabase()
,去doQuery()
獲取到查詢結果,然後存儲到緩存中。
SimpleExecutor:
@Override
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();
// 傳入參數創建StatementHanlder對象來執行查詢
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
// 創建jdbc中的statement對象
stmt = prepareStatement(handler, ms.getStatementLog());
// 執行 StatementHandler ,進行讀操作
return handler.query(stmt, resultHandler);
} finally {
// 關閉 StatementHandler 對象
closeStatement(stmt);
}
}
// 初始化 StatementHandler 對象
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
// 獲得 Connection 對象
Connection connection = getConnection(statementLog);
// 創建 Statement 或 PrepareStatement 對象
stmt = handler.prepare(connection, transaction.getTimeout());
// 設置 SQL 上的參數,例如 PrepareStatement 對象上的佔位符
handler.parameterize(stmt);
return stmt;
}
在doQuery()
中,會創建一個StatementHandler
執行在查詢,prepareStatement()
創建一個預編譯的對象返回。由handler執行查詢。到這一步執行器的任務就完成了,把查詢任務委派給了handler來處理。
StatementHandler
PreparedStatementHandler:
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
// 執行查詢
ps.execute();
// 處理返回結果
return resultSetHandler.handleResultSets(ps);
}
@Override
public void parameterize(Statement statement) throws SQLException {
//使用ParameterHandler對象來完成對Statement的設值
parameterHandler.setParameters((PreparedStatement) statement);
}
DefaultParameterHandler:
@SuppressWarnings("Duplicates")
@Override
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
// 遍歷 ParameterMapping 數組
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
for (int i = 0; i < parameterMappings.size(); i++) {
// 獲得 ParameterMapping 對象
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
// 獲得值
Object value;
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 {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
// 獲得 typeHandler、jdbcType 屬性
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
// 設置 ? 佔位符的參數
try {
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException | SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}
DefaultResultSetHandler:
//
// HANDLE RESULT SETS
//
// 處理 {@link java.sql.ResultSet} 結果集
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
// 多 ResultSet 的結果集合,每個 ResultSet 對應一個 Object 對象。而實際上,每個 Object 是 List<Object> 對象。
// 在不考慮存儲過程的多 ResultSet 的情況,普通的查詢,實際就一個 ResultSet ,也就是說,multipleResults 最多就一個元素。
final List<Object> multipleResults = new ArrayList<>();
int resultSetCount = 0;
// 獲得首個 ResultSet 對象,並封裝成 ResultSetWrapper 對象
ResultSetWrapper rsw = getFirstResultSet(stmt);
// 獲得 ResultMap 數組
// 在不考慮存儲過程的多 ResultSet 的情況,普通的查詢,實際就一個 ResultSet ,也就是說,resultMaps 就一個元素。
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount); // 校驗
while (rsw != null && resultMapCount > resultSetCount) {
// 獲得 ResultMap 對象
ResultMap resultMap = resultMaps.get(resultSetCount);
// 處理 ResultSet ,將結果添加到 multipleResults 中
handleResultSet(rsw, resultMap, multipleResults, null);
// 獲得下一個 ResultSet 對象,並封裝成 ResultSetWrapper 對象
rsw = getNextResultSet(stmt);
// 清理
cleanUpAfterHandlingResultSet();
// resultSetCount ++
resultSetCount++;
}
// 因爲 `mappedStatement.resultSets` 只在存儲過程中使用,本系列暫時不考慮,忽略即可
String[] resultSets = mappedStatement.getResultSets();
if (resultSets != null) {
while (rsw != null && resultSetCount < resultSets.length) {
ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
handleResultSet(rsw, resultMap, null, parentMapping);
}
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
}
// 如果是 multipleResults 單元素,則取首元素返回
return collapseSingleResultList(multipleResults);
}
// 處理 ResultSet ,將結果添加到 multipleResults 中
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
try {
// 暫時忽略,因爲只有存儲過程的情況,調用該方法,parentMapping 爲非空
if (parentMapping != null) {
handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
} else {
// 如果沒有自定義的 resultHandler ,則創建默認的 DefaultResultHandler 對象
if (resultHandler == null) {
// 創建 DefaultResultHandler 對象
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
// 處理 ResultSet 返回的每一行 Row
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
// 添加 defaultResultHandler 的處理的結果,到 multipleResults 中
multipleResults.add(defaultResultHandler.getResultList());
} else {
// 處理 ResultSet 返回的每一行 Row
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
}
}
} finally {
// issue #228 (close resultsets)
// 關閉 ResultSet 對象
closeResultSet(rsw.getResultSet());
}
}
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
// 創建 ResultLoaderMap 對象
final ResultLoaderMap lazyLoader = new ResultLoaderMap();
// 創建映射後的結果對象
Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
// 如果 hasTypeHandlerForResultObject(rsw, resultMap.getType()) 返回 true ,意味着 rowValue 是基本類型,無需執行下列邏輯。
if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
// 創建 MetaObject 對象,用於訪問 rowValue 對象
final MetaObject metaObject = configuration.newMetaObject(rowValue);
// foundValues 代表,是否成功映射任一屬性。若成功,則爲 true ,若失敗,則爲 false
boolean foundValues = this.useConstructorMappings;
/// 判斷是否開啓自動映射功能
if (shouldApplyAutomaticMappings(resultMap, false)) {
// 自動映射未明確的列
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
}
// 映射 ResultMap 中明確映射的列
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
// ↑↑↑ 至此,當前 ResultSet 的該行記錄的數據,已經完全映射到結果對象 rowValue 的對應屬性種
foundValues = lazyLoader.size() > 0 || foundValues;
// 如果沒有成功映射任意屬性,則置空 rowValue 對象。
// 當然,如果開啓 `configuration.returnInstanceForEmptyRow` 屬性,則不置空。默認情況下,該值爲 false
rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
}
return rowValue;
}
parameterHandler負責根據傳遞的參數值,對Statement對象設置參數,然後通過TypeHandler 完成jdbpType與javaType之間的轉換,對statement對象設置特定參數,
PreparedStatementHandler的query()方法對結果進行執行,交由resultSetHandler對返回結果進行處理,通過TypeHandler 對返回結果resultset取出特定的列,完成對結果封裝返回。
至此整個查詢結果就完成了。