MyBatis學習筆記
- 源碼篇
-
- 接口和對象介紹
-
- SqlSessionFactoryBuilder
- XMLConfigBuilder
- XMLMapperBuilder
- Configuration
- SqlSource接口
- SqlSessionFactory接口
- SqlSession接口
- Executor接口
-
- BaseExecutor
- CachingExecutor
- StatementHandler接口
-
- 定義
- 繼承結構
- prepare方法
- parameterize方法
- RoutingStatementHandler
- ParameterHandler接口
- ResultSetHandler接口
- 源碼閱讀
-
- 加載全局配置檔案(建立Configuration對象)流程
- 加載映射檔案流程
- SQL解析流程
- 擷取Mapper代理對象流程
- SqlSession執行主流程-執行SQL查詢流程
- 設定PreparedStatement的參數流程
- 結果集映射流程
- 待分析的問題
源碼篇
- 源碼閱讀的方法:找主線,找入口,記筆記(類名#方法名(資料成員變量)),參考其他人的源碼閱讀經驗。
- 源碼閱讀的目的:通過閱讀源碼,提升對設計模式的了解,提升程式設計能力;通過閱讀源碼,可以找到問題的根源,用來解決問題;應付面試。
接口和對象介紹
SqlSessionFactoryBuilder
XMLConfigBuilder
- 專門用來解析全局配置檔案的解析器
XMLMapperBuilder
- 專門用來解析映射檔案的解析器
Configuration
- Mybatis架構支援開發人員通過配置檔案與其進行交流,在配置檔案中配置的資訊,架構運作時,會被XMLConfigBuilder解析并存儲在一個Configuration對象中。
- Configuration對象會被作為參數傳給DefaultSqlSessionFactory,而DefaultSqlSessionFactory根據Configuration對象資訊為Client建立對應特征的SqlSession對象。
SqlSource接口
- DynamicSqlSource:主要是封裝動态SQL标簽解析之後的SQL語句和帶有${}的SQL語句
- RawSqlSource:主要封裝帶有#{}的SQL語句
- StaticSqlSource:是BoundSql中要存儲SQL語句的一個載體,上面兩個SqlSource的SQL語句,最終都會存儲到該SqlSource實作類中。
SqlSessionFactory接口
- 預設實作類是DefaultSqlSessionFactory類
SqlSession接口
- 預設實作類是DefaultSqlSession類
Executor接口
- 定義:
public interface Executor {
ResultHandler NO_RESULT_HANDLER = null;
int update(MappedStatement ms, Object parameter) throws SQLException;
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
<E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;
List<BatchResult> flushStatements() throws SQLException;
void commit(boolean required) throws SQLException;
void rollback(boolean required) throws SQLException;
CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
boolean isCached(MappedStatement ms, CacheKey key);
void clearLocalCache();
void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);
Transaction getTransaction();
void close(boolean forceRollback);
boolean isClosed();
void setExecutorWrapper(Executor executor);
}
- Mybatis中所有的Mapper語句的執行都是通過Executor進行的,Executor是Mybatis的一個核心接口。
- 從其定義的接口方法我們可以看出,對應的增删改語句是通過Executor接口的update方法進行的,查詢是通過query方法進行的。
- Executor是跟SqlSession綁定在一起的,每一個SqlSession都擁有一個新的Executor對象,由Configuration建立,代碼如下:
public Executor newExecutor(Transaction transaction) {
return newExecutor(transaction, defaultExecutorType);
}
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;
}
- Executor分成兩大類:
- BaseExecutor:抽像類,在建立時會根據傳過來的ExecutorType建立不同的類:SimpleExecutor、ReuseExecutor、BatchExecutor。
- CachingExecutor:先從緩存中擷取查詢結果,存在就傳回,不存在,再委托給Executor delegate去資料庫取,delegate可以是 BaseExecutor 中的任一一種。
BaseExecutor
- SimpleExecutor:每執行一次update或select,就開啟一個Statement對象,用完立刻關閉Statement對象。(可以是Statement或PrepareStatement對象)
- ReuseExecutor:執行update或select,以sql作為key查找Statement對象,存在就使用,不存在就建立,用完後,不關閉Statement對象,而是放置于Map<String, Statement>内,供下一次使用(可以是Statement或PrepareStatement對象)。
- 代碼如下:
@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
Statement stmt = prepareStatement(handler, ms.getStatementLog());
return handler.update(stmt);
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
BoundSql boundSql = handler.getBoundSql();
String sql = boundSql.getSql();
if (hasStatementFor(sql)) {
stmt = getStatement(sql);
applyTransactionTimeout(stmt);
} else {
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
putStatement(sql, stmt);
}
handler.parameterize(stmt);
return stmt;
}
private boolean hasStatementFor(String sql) {
try {
return statementMap.keySet().contains(sql) && !statementMap.get(sql).getConnection().isClosed();
} catch (SQLException e) {
return false;
}
}
private Statement getStatement(String s) {
return statementMap.get(s);
}
private void putStatement(String sql, Statement stmt) {
statementMap.put(sql, stmt);
}
//...
- BatchExecutor:執行update(沒有select,JDBC批處理不支援select),将所有sql都添加到批進行中(addBatch()),等待統一執行(executeBatch()),它緩存了多個Statement對象,每個Statement對象都是addBatch()完畢後,等待逐一執行executeBatch()批處理的;BatchExecutor相當于維護了多個桶,每個桶裡都裝了很多屬于自己的SQL,就像蘋果籃裡裝了很多蘋果,番茄籃裡裝了很多番茄。最後,再統一倒進倉庫。(可以是Statement或PrepareStatement對象)。
CachingExecutor
- 代碼如下:
public CachingExecutor(Executor delegate) {
this.delegate = delegate;
delegate.setExecutorWrapper(this);
}
//...
@Override
public int update(MappedStatement ms, Object parameterObject) throws SQLException {
flushCacheIfRequired(ms);
return delegate.update(ms, parameterObject);
}
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
Cache cache = ms.getCache();
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
...
StatementHandler接口
- 首先約定文中講的四大對象是指:Executor、StatementHandler、ParameterHandler 和 ResultHandler 接口對象。而StatementHandler 毫無疑問是四大對象中最重要的一個,它的任務就是和資料庫對話。在它這裡會使用 ParameterHandler 和ResultHandler 對象為我們綁定SQL參數群組裝最後的結果傳回。
定義
public interface StatementHandler {
Statement prepare(Connection connection, Integer transactionTimeout)
throws SQLException;
void parameterize(Statement statement)
throws SQLException;
void batch(Statement statement)
throws SQLException;
int update(Statement statement)
throws SQLException;
<E> List<E> query(Statement statement, ResultHandler resultHandler)
throws SQLException;
<E> Cursor<E> queryCursor(Statement statement)
throws SQLException;
BoundSql getBoundSql();
ParameterHandler getParameterHandler();
}
- 這裡有幾個重要的方法:prepare、parameterize、query、update,他們的作用是不一樣的。
繼承結構
- StatementHandler:頂層接口。
- BaseStatementHandler : 實作頂層接口的抽象類,實作了部分接口,并定義了一個抽象方法。
- SimpleStatementHandler:對應JDBC中常用的Statement接口,用于簡單SQL的處理。
- PreparedStatementHandler:對應JDBC中的PreparedStatement,預編譯SQL的接口。
- CallableStatementHandler:對應JDBC中CallableStatement,用于執行存儲過程相關的接口。
- RoutingStatementHandler:這個接口是以上三個接口的路由,沒有實際操作,隻是負責上面三個StatementHandler的建立及調用。
在MyBatis中,Configuration對象會采用new RoutingStatementHandler()來生成StatementHandler對象,換句話說我們真正使用的是RoutingStatementHandler對象,然後它會根據Executor的類型去建立對應具體的statementHandler對象(SimpleStatementHandler,PreparedStatementHandler和CallableStatementHandler)。然後利用具體statementHandler的方法完成所需要的功能。那麼這個具體的statementHandler是儲存在RoutingStatementHandler對象的delegate屬性的,是以當我們攔截statementHandler的時候就要常常通路它了。
prepare方法
- BaseStatementHandler的三個子類都繼承 prepare() 方法,并沒有重寫該方法。該方法中調用了一個抽象方法 instantiateStatement()。根據一個 Connection 傳回一個Statement 對象;三個子類都實作了該方法,分别傳回了 Statement,PrepareStaement 和 CallableStatement 對象。
@Override
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);
}
}
protected abstract Statement instantiateStatement(Connection connection) throws SQLException;
- 顯然我們通過源碼更加關注抽象方法instantiateStatement是做了什麼事情。它依舊是一個抽象方法,那麼它就有其實作類。
- 那就是之前說的那幾個具體的StatementHandler對象,讓我們看看PreparedStatementHandler:
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
// 擷取帶有占位符的SQL語句
String sql = boundSql.getSql();
if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
// 處理帶有主鍵傳回的SQL
String[] keyColumnNames = mappedStatement.getKeyColumns();
if (keyColumnNames == null) {
return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
} else {
return connection.prepareStatement(sql, keyColumnNames);
}
} else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
return connection.prepareStatement(sql);
} else {
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
}
}
這個方法非常簡單,我們可以看到它主要是根據上下文來預編譯SQL,這時我們還沒有設定參數。設定參數的任務是交由statement接口的parameterize方法來實作的。
parameterize方法
- 進行參數設定:
@Override
public void parameterize(Statement statement) throws SQLException {
// 通過ParameterHandler處理參數
parameterHandler.setParameters((PreparedStatement) statement);
}
- 抽象類的三個子類都實作了 update()、batch()、query(),在對應的方法中将傳入的 Statement 對象轉型為 具體的 Statement 類型進行相應的操作。
- 以PreparedStatementHandler的實作為例:
@Override
public int update(Statement statement) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
int rows = ps.getUpdateCount();
Object parameterObject = boundSql.getParameterObject();
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
return rows;
}
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
// 執行PreparedStatement,也就是執行SQL語句
ps.execute();
// 處理結果集
return resultSetHandler.handleResultSets(ps);
}
- 我們可以看到如果是進行update的,它将會執行生成主鍵的操作(插入資料要自動生成主鍵的時候),然後就傳回影響行數。
- 如果是進行query的就更加簡單了,它就是執行SQL語句,然後将結果使用resultHandler的handleResultSets去完成我們的結果組裝。
RoutingStatementHandler
- 該類采用了代理模式(靜态代理),直接實作了 StatmentHandler 接口,實作了接口的全部方法。該類持有一個 StatmentHandler 的執行個體,并在建立該類時根據傳入的 MappedStatement 的 type 建立不同的 Statement 執行個體,然後通過這個具體的執行個體去實作相應的操作。
public class RoutingStatementHandler implements StatementHandler {
private final StatementHandler delegate;
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
switch (ms.getStatementType()) {
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
}
@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
return delegate.prepare(connection, transactionTimeout);
}
@Override
public void parameterize(Statement statement) throws SQLException {
delegate.parameterize(statement);
}
@Override
public void batch(Statement statement) throws SQLException {
delegate.batch(statement);
}
// 其他方法同理
...
}
ParameterHandler接口
- 定義
public interface ParameterHandler {
Object getParameterObject(); // 擷取參數對象
void setParameters(PreparedStatement ps)
throws SQLException; // 設定參數對象
}
- 繼承結構:隻有一個預設的實作類
- 預設實作類:DefaultParameterHandler
public class DefaultParameterHandler implements ParameterHandler {
private final TypeHandlerRegistry typeHandlerRegistry;
private final MappedStatement mappedStatement;
private final Object parameterObject;
private final BoundSql boundSql;
private final Configuration configuration;
public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
this.mappedStatement = mappedStatement;
this.configuration = mappedStatement.getConfiguration();
this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
this.parameterObject = parameterObject;
this.boundSql = boundSql;
}
@Override
public Object getParameterObject() {
return parameterObject;
}
@Override
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
// 擷取要設定的參數映射資訊
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
for (int i = 0; i < parameterMappings.size(); i++) {
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 typeHandler = parameterMapping.getTypeHandler();
// 擷取每個參數的JdbcType
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
// 給PreparedStatement設定參數
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
} catch (SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}
}
- 它的 setParameters() 隻在兩處地方被調用,分别為 PreparedStatementHandler 和 CallableStatementHandler 的 parameterize()。
ResultSetHandler接口
- 定義:
public interface ResultSetHandler {
// 将Statement執行後産生的結果集(可能有多個結果集)映射為結果清單
<E> List<E> handleResultSets(Statement stmt) throws SQLException;
<E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
// 處理存儲過程執行後的輸出參數
void handleOutputParameters(CallableStatement cs) throws SQLException;
}
- 繼承結構
- 預設實作類:ResultSetHandler的具體實作類是DefaultResultSetHandler,其實作的步驟就是将Statement執行後的結果集,按照Mapper檔案中配置的ResultType或ResultMap來封裝成對應的對象,最後将封裝的對象傳回 。
源碼閱讀
- Mybatis整體架構
- MyBatis源碼包對應的架構圖
加載全局配置檔案(建立Configuration對象)流程
- 找入口:SqlSessionFactoryBuilder#build()
/**
* 主線一:加載全局配置檔案流程
*/
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
// XMLConfigBuilder 用來解析XML配置檔案
// 使用建構者模式
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
// parser.parse():使用XPath解析XML配置檔案,将配置檔案資訊封裝為Configuration對象
// 傳回 DefaultSqlSessionFactory 對象,改對象擁有Configuration對象(封裝了配置檔案資訊的)
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.
}
}
}
- 記筆記
SqlSessionFactoryBuilder#build(...):用于建構SqlSessionFactory對象
|--XMLConfigBuilder#構造函數(...):用來解析全局檔案的解析器
|--XPathParser#構造函數(...):用來使用XPath文法解析XML的解析器
|--XPathParser#createDocument(...):解析全局配置檔案,封裝為Document對象(封裝一些子節點,使用XPath文法解析擷取)
|--Configuration#構造函數(...):建立Configuration對象,同時初始化内置類的别名
|--XMLConfigBuilder#parse(...):全局配置檔案的解析器
|--XPathParser#parse(XPath文法):XPath解析器,專門用來通過XPath文法解析XML傳回XNode節點
|--XMLConfigBuilder#parseConfiguration(XNode):從全局配置檔案根節點開始解析,加載的資訊設定到Configuration對象中
|--SqlSessionFactoryBuilder#build(...):建立SqlSessionFactory接口的預設實作類DefaultSqlSessionFactoryBuilder
- 總結:
- SqlSessionFactoryBuilder建立SqlSessionFactory時,需要傳入一個Configuration對象
- XMLConfigBuilder對象會去執行個體化Configuration
- XMLConfigBuilder對象會去初始化Configuration對象
- 通過XPathParser去解析全局配置檔案,形成Document對象
- 通過XPathParser去擷取指定節點的XNode對象
- 解析XNode對象的資訊,然後封裝到Configuration對象中
- 主要涉及到的類:
SqlSessionFactoryBuilder
XMLConfigBuilder
XPathParser
Configuration
加載映射檔案流程
- 找入口:XMLConfigBuilder#mapperElement()
/**
* 主線二:解析<mappers>标簽
*/
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
// 擷取<mappers>标簽的子标簽
for (XNode child : parent.getChildren()) {
// <package>子标簽
if ("package".equals(child.getName())) {
// 擷取mapper接口和mapper映射檔案對應的package包名
String mapperPackage = child.getStringAttribute("name");
// 将包下所有的mapper接口以及它的代理對象存儲到一個Map集合中
// key為mapper接口類型,value為代理對象工廠
configuration.addMappers(mapperPackage);
} else {// <mapper>子标簽
// 擷取<mapper>子标簽的resource屬性
String resource = child.getStringAttribute("resource");
// 擷取<mapper>子标簽的url屬性
String url = child.getStringAttribute("url");
// 擷取<mapper>子标簽的class屬性
String mapperClass = child.getStringAttribute("class");
// 按照 resource --> url --> class 的優先級去解析<mapper>子标簽,他們是互斥的
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
// XMLMapperBuilder專門用來解析mapper映射檔案
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
// XMLMapperBuilder調用parse方法解析mapper映射檔案
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
// XMLMapperBuilder調用parse方法解析mapper映射檔案
mapperParser.parse();
} else if (resource == null && url == null && mapperClass != null) {
Class<?> mapperInterface = Resources.classForName(mapperClass);
// 将指定mapper接口以及它的代理對象存儲到一個Map集合中
// key為mapper接口類型,value為代理對象工廠
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
- 記筆記
XMLConfigBuilder#mapperElement(...):解析全局配置檔案的<mappers>标簽
|--XMLMapperBuilder#構造函數(...):專門用來解析映射檔案
|--XPathParser#構造函數(...):
|--XPathParser#createDocument(...):解析映射檔案,封裝為Document對象
|--MapperBuilderAssistant#構造函數(...):用于建構MappedStatement對象
|--XMLMapperBuilder#parse(...):解析方法
|--XMLMapperBuilder#configurationElement(...):專門用來解析mapper映射檔案
|--XMLMapperBuilder#buildStatementFromContext(...):用來建立MappedStatement對象的
|--|--XMLMapperBuilder#buildStatementFromContext(...):
|--XMLStatementBuilder#構造函數(...):專門用來解析MappedStatement
|--XMLStatementBuilder#parseStatementNode(...):
|--MapperBuilderAssistant#addMappedStatement(...):建立MappedStatement
|--MappedStatement.Builder#構造函數(...)
|--MappedStatement#build(...):建立MappedStatement對象,并存儲到Configuration對象中
- 主要涉及到的類:
XMLConfigBuilder
XMLMapperBuilder
XPathParser
MapperBuilderAssistant
XMLStatementBuilder
MappedStatement.Builder
MappedStatement
SQL解析流程
- 找入口:XMLLanguageDriver#createSqlSource
/**
* 主線三:SQL解析
*/
@Override
public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
// 初始化了動态SQL标簽處理器
XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
// 解析動态SQL
return builder.parseScriptNode();
}
- 記筆記
XMLLanguageDriver#createSqlSource(...):擷取解析之後的SQL資訊,以及參數資訊
|--XMLScriptBuilder#構造函數(...):初始化動态SQL中的節點處理器集合
|--XMLScriptBuilder#parseScriptNode(...):
|--XMLScriptBuilder#parseDynamicTags(...):解析動态SQL标簽
|--SqlSource#getBoundSql(...)
|--SqlSourceBuilder#parse(...):将帶有#{}的SQL語句進行解析,然後封裝到StaticSqlSource中
- 主要涉及到的類:
XMLLanguageDriver
XMLScriptBuilder
SqlSource
SqlSourceBuilder
擷取Mapper代理對象流程
- 找入口:DefaultSqlSession#getMapper(…)
/**
* 主線四:擷取Mapper代理對象
*/
@Override
public <T> T getMapper(Class<T> type) {
// 從Configuration對象中,根據Mapper接口,擷取Mapper代理對象
return configuration.<T>getMapper(type, this);
}
- 記筆記
DefaultSqlSession#getMapper(...):擷取Mapper代理對象
|--Configuration#getMapper(...):擷取Mapper代理對象
|--MapperRegistry#getMapper(...):通過代理對象工廠,擷取代理對象
|--MapperProxyFactory#newInstance(...):調用JDK的動态代理方式,建立Mapper代理
- 主要涉及到的類:
DefaultSqlSession
Configuration
MapperRegistry
MapperProxyFactory
SqlSession執行主流程-執行SQL查詢流程
- 找入口:DefaultSqlSession#selectList(…)
/**
* 主線五:SqlSession執行
*/
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
// 根據傳入的statementId,擷取MappedStatement對象
MappedStatement ms = configuration.getMappedStatement(statement);
// 調用執行器的查詢方法
// RowBounds是用來邏輯分頁(按照條件将資料從資料庫查詢到記憶體中,在記憶體中進行分頁)
// wrapCollection(parameter)是用來裝飾集合或者數組參數
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();
}
}
- 記筆記
DefaultSqlSession#selectList(...)
|--CachingExecutor#query(...)
|--BaseExecutor#query(...)
|--BaseExecutor#queryFromDatabase(...)
|--SimpleExecutor#doQuery(...)
|--Configuration#newStatementHandler(...):建立StatementHandler用來執行MappedStatement對象
|--RoutingStatementHandler#構造函數(...):根據路由規則,設定不同的StatementHandler
|--SimpleExecutor#prepareStatement(...):主要設定PreparedStatement的參數
|--SimpleExecutor#getConnection(...):擷取資料庫連接配接
|--PreparedStatementHandler#prepare(...):建立PreparedStatement對象
|--PreparedStatementHandler#parameterize:設定PreparedStatement的參數
|--PreparedStatementHandler#query(...):主要用來執行SQL語句,以及處理結果集
|--PreparedStatement#execute():調用JDBC的API執行Statement
|--DefaultResultSetHandler#handleResultSets:處理結果集
- 主要涉及到的類:
DefaultSqlSession
CachingExecutor
BaseExecutor
SimpleExecutor
Configuration
RoutingStatementHandler
PreparedStatementHandler
PreparedStatement
DefaultResultSetHandler
設定PreparedStatement的參數流程
- 找入口:PreparedStatementHandler#parameterize(…)
/**
* 主線六:PreparedStatement 進行參數設定
*/
@Override
public void parameterize(Statement statement) throws SQLException {
// 通過ParameterHandler處理參數
parameterHandler.setParameters((PreparedStatement) statement);
}
- 記筆記
PreparedStatementHandler#parameterize(...):設定PreparedStatement的參數
|--DefaultParameterHandler#setParameters(...):設定參數
|--BaseTypeHandler#setParameter(...):
|--XxxTypeHandler#setNonNullParameter(...):調用PreparedStatement的setXxx方法
- 主要涉及到的類:
PreparedStatementHandler
DefaultParameterHandler
BaseTypeHandler
XxxTypeHandler
結果集映射流程
- 找入口:DefaultResultSetHandler#handleResultSets(…)
/**
* 主線七:結果集映射
*/
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
// <select>标簽的resultMap屬性,可以指定多個值,多個值之間用逗号(,)分割
final List<Object> multipleResults = new ArrayList<>();
int resultSetCount = 0;
// 這裡是擷取第一個結果集,将傳統JDBC的ResultSet包裝成一個包含結果列元資訊的ResultSetWrapper對象
ResultSetWrapper rsw = getFirstResultSet(stmt);
// 這裡是擷取所有要映射的ResultMap(按照逗号分割出來的)
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
// 要映射的ResultMap的數量
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);
// 循環處理每個ResultMap,從第一個開始處理
while (rsw != null && resultMapCount > resultSetCount) {
// 得到結果映射資訊
ResultMap resultMap = resultMaps.get(resultSetCount);
// 處理結果集
// 從rsw結果集參數中擷取查詢結果,再根據resultMap映射資訊,将查詢結果映射到multipleResults中
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
// 對應<select>标簽的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++;
}
}
// 如果隻有一個結果集合,則直接從多結果集中取出第一個
return collapseSingleResultList(multipleResults);
}
- 記筆記
DefaultResultSetHandler#handleResultSets(...)
|--DefaultResultSetHandler#handleResultSet(...)
|--DefaultResultSetHandler#handleRowValues(...)
|--DefaultResultSetHandler#handleRowValuesForNestedResultMap(...)
|--DefaultResultSetHandler#getRowValue(...)
|--DefaultResultSetHandler#createResultObject(...):建立映射結果對象
|--DefaultResultSetHandler#applyAutomaticMappings(...)
|--DefaultResultSetHandler#applyPropertyMappings(...)
- 主要涉及到的類:
DefaultResultSetHandler
- resultType最終會封裝成resultMap
待分析的問題
- 延遲加載的流程
- 嵌套結果集映射流程