傳統(tǒng)方式源碼剖析
源碼剖析-初始化陈轿,點進build方法可以看下
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
//這一行代碼正是初始化工作的開始
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
進入源碼分析:
// 1.最初調(diào)用的build
public SqlSessionFactory build(InputStream inputStream) {
// 調(diào)用了重載方法
return this.build((InputStream)inputStream, (String)null, (Properties)null);
}
//2.調(diào)用的重載方法
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
SqlSessionFactory var5;
try {
//XMLConfigBuilder是專門解析mybatis的配置文件的類
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
//這里又調(diào)用了一個重載方法拱燃,parser.parse()返回的是configuration對象
var5 = this.build(parser.parse());
} catch (Exception var14) {
throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException var13) {
}
}
return var5;
}
Mybatis在初始化的時候,會將Mybatis的配置信息全部加載到內(nèi)存中沸久,使用org.apache.ibatis.session.Configuration實例來維護坛缕。
下面進入對配置文件解析部分:
首先對Configuration對象進行介紹:
Configuration對象的結(jié)構(gòu)和xml配置文件的對象基本相同。
回顧一下xml在配置有哪些:
properties(屬性)胡诗,settings(設(shè)置)邓线,typeAliass(類型別名),typeHandless(類型處理器)煌恢,objectFactory(對象工廠)骇陈,mapper(映射器)等。Configuration也有相應(yīng)的對象屬性來封裝它們瑰抵。
也就是說你雌,初始化配置文件信息的本質(zhì)就是創(chuàng)建Configuration對象,將解析的xml數(shù)據(jù)封裝到Configuration內(nèi)部屬性中二汛。
觀察一下上面源碼中的parser.parse() (XMLConfigBuilder#parse
//解析xml成Configuration對象
public Configuration parse() {
//若已解析婿崭,拋出BuilderException異常
if (this.parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
} else {
//標(biāo)識已解析
this.parsed = true;
//解析 xml configuration 節(jié)點 this.parseConfiguration(this.parser.evalNode("/configuration"));
return this.configuration;
}
}
XMLConfigBuilder#parseConfiguration
private void parseConfiguration(XNode root) {
try {
/*
*下邊的代碼基本都是都是解析標(biāo)簽
*/
this.propertiesElement(root.evalNode("properties"));
Properties settings = this.settingsAsProperties(root.evalNode("settings"));
this.loadCustomVfs(settings);
this.typeAliasesElement(root.evalNode("typeAliases"));
this.pluginElement(root.evalNode("plugins"));
this.objectFactoryElement(root.evalNode("objectFactory"));
this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
this.settingsElement(settings);
this.environmentsElement(root.evalNode("environments"));
this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
this.typeHandlerElement(root.evalNode("typeHandlers"));
this.mapperElement(root.evalNode("mappers"));
} catch (Exception var3) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
}
}
介紹一下MappedStatement:
作用:MappedStatement和Mapper配置文件中的一個select/update/insert/delete節(jié)點相對應(yīng)。mapper中配置的標(biāo)簽都被封裝到了此對象中肴颊,主要用途是描述一條SQL語句氓栈。
初始化過程:回顧剛開始介紹的加載配置文件的過程中,會對mybatis-config.xml中的各個標(biāo)簽進行解析婿着,其中mappers標(biāo)簽都用來引入mapper.xml文件或者配置mapper接口的目錄授瘦。
<select id ="getUser" resultType="user">
select * from user where id=#{id}
</select>
這樣一個select標(biāo)簽會在初始化配置文件時被封裝成一個MappedStatement對象,然后存儲在Configuration對象的mappedStatements中
protected final Map<String, MappedStatement> mappedStatements;
...
this.mappedStatements = new Configuration.StrictMap("Mapped Statements collection");
mappedStatements 是一個HashMap竟宋,存儲時key=全限定類名+方法名提完,value=對應(yīng)的MappedStatement對象。
- 在XMLConfigBuilder中處理:(XMLConfigBuilder#parseConfiguration
this.mapperElement(root.evalNode("mappers"));
到此對xml配置文件的解析就結(jié)束了袜硫,回到步驟2調(diào)用的重載build方法(SqlSessionFactoryBuilder#build
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
源碼剖析-執(zhí)行SQL流程
先簡單介紹一下SqlSession:
SqlSession是一個接口氯葬,它有兩個實現(xiàn)類: DefaultSqlSession(默認(rèn))和SqlSessionManager(棄用不做介紹)
SqlSessions是MyBatis用于和數(shù)據(jù)庫交互的頂層類,通常它與ThreadLocal綁定婉陷,一個會話使用一個SqlSession帚称,并且在使用完畢后需要close
Executor:
Executor是一個接口官研,他有三個常用的實現(xiàn)類
BatchExecutor(重用語句執(zhí)行并執(zhí)行批量更新)
ReuseExecutor(重用預(yù)處理語句prepared statements)
SimpleExecutor(普通的執(zhí)行器)
繼續(xù)分析,初始化完畢后闯睹,我們就要執(zhí)行SQL了
SqlSession sqlSession = factory.openSession();
List<User> list =
sqlSession.selectList("com.lagou.mapper.UserMapper.getUserByName");
SqlSessionFactory#openSession
public SqlSession openSession() {
//getDefaultExecutorType 傳遞的是SimpleExecutor
return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false);
}
...
// 進入openSessionFromDataSource
// ExecutorType 為Executor的類型戏羽,TransactionIsolationLevel 為事務(wù)隔離級別,autoCommit為是否開啟事務(wù)
//openSessionFromDataSource的重載方法可以指定獲得的SqlSession類型和事務(wù)處理
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//根據(jù)參數(shù)創(chuàng)建指定類型的Executor
final Executor executor = configuration.newExecutor(tx, execType);
//返回的是DefaultSqlSession
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();
}
}
執(zhí)行sqlsession的api - 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 {
//根據(jù)傳入的全限定名+方法從映射的Map中取出MappedStatenent對象
MappedStatement ms = configuration.getMappedStatement(statement);
//調(diào)用Executor中的方法處理
//RowBounds是用來邏輯分頁的
//wrapCollection用來裝飾集合或者數(shù)組參數(shù)
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();
}
}
源碼剖析-executor
繼續(xù)源碼中的步驟楼吃,進入BaseExecutor#query()
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//根據(jù)傳入的參數(shù)動態(tài)獲取sql始花,最后返回用BoundSql對象表示
BoundSql boundSql = ms.getBoundSql(parameter);
//為本次查詢創(chuàng)建查詢緩存
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
//進入query的重載方法
@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());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
queryStack++;
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
//如果緩存中沒有本次查詢的值,那么從數(shù)據(jù)庫中查詢
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
去數(shù)據(jù)庫查詢
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
//查詢的方法
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
localCache.removeObject(key);
}
//查詢結(jié)果放入緩存中
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
doQuery方法(SimpleExecutor#doQuery)
@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();
//傳入?yún)?shù)創(chuàng)建StatementHandler對象來執(zhí)行查詢
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//創(chuàng)建jdbc的statement對象
stmt = prepareStatement(handler, ms.getStatementLog());
//statement進行處理
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
創(chuàng)建Statement的方法(SimpleExecutor#prepareStatement
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
//這條代碼中的getConnection方法經(jīng)過重重調(diào)用最后會調(diào)用openConnection方法孩锡,從連接池中獲取到連接
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
JdbcTransaction#openConnection
protected void openConnection() throws SQLException {
if (log.isDebugEnabled()) {
log.debug("Opening JDBC Connection");
}
//從連接池獲得連接
connection = dataSource.getConnection();
if (level != null) {
connection.setTransactionIsolation(level.getLevel());
}
setDesiredAutoCommit(autoCommmit);
}
上述的Executor.query()方法幾經(jīng)轉(zhuǎn)折酷宵,最后會創(chuàng)建一個StatementHandler對象,然后將必要的參數(shù)傳遞給StatementHandler躬窜,使用StatementHandler來完成對數(shù)據(jù)庫的查詢浇垦,最終返回List結(jié)果集。
從上述的代碼我們可以看除荣挨,Executor的功能和作用:
- 根據(jù)傳遞的參數(shù)男韧,完成對SQL語句的動態(tài)解析,生成BoundSql對象默垄,供StatementHandler使用
- 為查詢創(chuàng)建緩存此虑,以提高性能
- 創(chuàng)建JDBC的statement連接對象,傳遞給 StatementHandler對象口锭,返回List查詢結(jié)果
源碼剖析-StatementHandler
StatementHandler對象主要完成兩個工作:
- 對JDBC的preparedStatement類型的對象朦前,創(chuàng)建的過程中,我們使用的SQL語句字符串會包含若干個“讹弯?”占位符况既,我們在其后再對占位符進行設(shè)值这溅。StatementHandler通過parametize(statement)方法對Statenebr進行設(shè)值组民。
- StatementHandler通過List query(Statement statement, ResultHandler resultHandler) 方法來完成執(zhí)行statement,和將statement對象返回的resultSet封裝成List;
進入到StatementHandler的parameterize(statement)方法的實現(xiàn)(PreparedStatementHandler#parameterize
@Override
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}
//對某一個Statement設(shè)置參數(shù)
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);
}
//每一個mapping都有一個TypeHandler悲靴,根據(jù)TypeHandler來對preparedStatement進行設(shè)置參數(shù)
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
//設(shè)置參數(shù)
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);
}
}
}
}
}
從上述代碼可以看到臭胜,StatementHandler的parameterize(Statement)方法調(diào)用了ParameterHandler.setParameters方法
ParameterHandler的setParameters方法負(fù)責(zé)根據(jù)我們輸入的參數(shù),對statement對象的“癞尚?”占位符處進行賦值耸三。
進入到StatementHandler的<E> List<E> query(Statement statement, ResultHandler resultHandler)方法的實現(xiàn)
PreparedStatementHandler#query
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
//調(diào)用PreparedStatement.execute方法,然后將resultSet交給resultSetHandler處理
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
//使用resultSetHandler來處理resultSet
return resultSetHandler.<E> handleResultSets(ps);
}
resultSetHandler的handleResultSets方法會將Statement語句執(zhí)行后生成的resultSet結(jié)果集轉(zhuǎn)換成List結(jié)果集浇揩,源碼
DefaultResultSetHandler#handleResultSets
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
//多resultSet結(jié)果集合仪壮,每一個ResultSet對應(yīng)一個Object對象,而實際上胳徽,每一個Object是List<Object>對象
//在不考慮存儲過程的多resultSet的情況积锅,普通的查詢爽彤,實際就是一個resultSet,也就是說multipleResults 最多就一個元素
final List<Object> multipleResults = new ArrayList<Object>();
int resultSetCount = 0;
//獲得首個resultSet對象,并封裝成ResultSetWrapper 對象
ResultSetWrapper rsw = getFirstResultSet(stmt);
//獲取resultMaps數(shù)組
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
//校驗
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
//獲得ResultMap對象
ResultMap resultMap = resultMaps.get(resultSetCount);
//處理ResultSet缚陷,將結(jié)果添加multipleResults中
handleResultSet(rsw, resultMap, multipleResults, null);
//獲得下一個ResultSet對象适篙,并封裝成ResultSetWrapper對象
rsw = getNextResultSet(stmt);
//清理
cleanUpAfterHandlingResultSet();
//resultSetCount++
resultSetCount++;
}
//mappedStatement的resultSet 只在存儲過程中使用 ,暫時不考慮
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);
}