Mybatis原始碼分析-整體設計(一)
SSM是目前常見的構建Web專案的方案,Mybatis是其中重要的一環,如果能深刻的理解Mybatis的內部原理,對我們會有極大的幫助,接下來一起看看Mybatis的內部設計。
準備
-
搭建Mybatis的基本執行環境,參考
- 貼上自己的程式碼
public static void main(String[] args) {
SqlSession sqlSession = null;
try {
InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 查詢資料庫內容
sqlSession = sqlSessionFactory.openSession();
User user = sqlSession.selectOne("me.aihe.dao.UserMapper.selectUser",1);
System.out.println(user);
// 插入資料庫內容
} catch (IOException e) {
e.printStackTrace();
}finally {
if (sqlSession != null) {
sqlSession.close();
}
}
}
分析
- 獲取Mybatis的配置檔案,內部透過ClassLoader載入檔案流,這一步需要對Classloader有一定的理解,裡面相對簡單,就不多說了
Resources.getResourceAsStream("mybatis.xml");
- 建立SqlSessionFactory, 透過JDK內部的w3c解析配置檔案的內容,封裝到Configration物件中,最後透過Configuration來建立DefaultSqlSessionFactory.
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
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.
}
}
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
- 透過SqlSessionFactory建立SqlSession物件
- 獲取配置的env引數,在解析配置的時候根據配置生成了TransactionFactory
- TransactionFactory物件,在設定env後,預設是JDBCTransactionFactory
- 透過配置生成預設的executor,executor是很重要的元件,可以看到executor封裝了很多運算元據庫相關的東西
- 生成預設的DefaultSqlSession,內部有executor,TransactionFactory,configuration
@Override
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
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);
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();
}
}
- SqlSession透過key獲取到與key繫結的sql語句,並且執行,最後獲取到結果。這步內部可以細分
sqlSession.selectOne("me.aihe.dao.UserMapper.selectUser",1);
selectOne內部呼叫的是selectList的函式,selectList函式內部,首先獲取到key對應的MappedStatement,然後透過Executor查詢MapperStatement。
在解析配置的時候建立key與MappedStatement對映關係的
@Override
public List selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
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();
}
}
- 不同的executor內部的查詢方法不同,後面我們詳細講解不同的executor。這次先看SimpleExecutor.
- queryStack判斷當前的SQL執行棧,可能會連續執行多條sql語句
- localCache 快取當前Sqlsession查詢到的物件
- queryFromDatabase 如果沒有快取從資料庫中進行查詢,重點語句
@Override
public List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
@SuppressWarnings("unchecked")
@Override
public List 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 list;
try {
queryStack++;
list = resultHandler == null ? (List) localCache.getObject(key) : null;
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
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;
}
- query方法中真正的查詢交給子類的doQuery,而SimpleExecutor的doQuery如下
- 新建StatementHandler,語句處理器
- 透過StatementHandler再進行query
- Statement為JDK提供的SQL介面,獲取Statement經歷了StatementHandler.prepare,然後parameterize設定引數。
- query方法利用MySQL內部的query
- 獲取到結果之後,透過resultHandler處理獲取到的結果
@Override
public List 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.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
- StatementHandler內部的呼叫,結果集處理使用的是DefaultResultSetHandler,內部ResultSet的處理屬於JDBC的知識,想看懂Mybatis,對JDBC也要有一定的理解
- 獲取到結果返回給我們
User user = sqlSession.selectOne("me.aihe.dao.UserMapper.selectUser",1);
回顧
這次大致看了下Mybatis的基本執行流程,涉及到了幾個關鍵的類
- SqlSessionFactory ,SqlSession
- MappedStatement 封裝我們寫的SQL語句
- StatementHandler 語句處理器
- ResultHandler 返回的結果處理器
- Executor Mybatis中的語句執行器
建立SqlSessionFactory -> 獲取SqlSession -> 獲取->MappedStatement -> 獲取StatementHandler同時建立Statement -> 執行Statement -> 使用ResultSet處理執行的結果,處理結果根據Mapperr.xml檔案中指定的型別對映最終實現ORM功能
總結
Mybatis的過程相比Spring MVC更直觀一些,不過需要熟悉JDBC的知識,內部還有一些細節,後續繼續研究
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2508/viewspace-2812745/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Libev原始碼分析 -- 整體設計原始碼
- 精盡 MyBatis 原始碼分析 - 整體架構MyBatis原始碼架構
- MyBatis原始碼窺探(一):MyBatis整體架構解析MyBatis原始碼架構
- Mybatis原始碼分析(一)Mybatis的架構設計簡介MyBatis原始碼架構
- MyBatis原始碼分析(一)MyBatis原始碼
- 微信MMKV原始碼分析(一) | 整體流程原始碼
- OkHttp 3.7原始碼分析(一)——整體架構HTTP原始碼架構
- 深度 Mybatis 3 原始碼分析(一)SqlSessionFactoryBuilder原始碼分析MyBatis原始碼SQLSessionUI
- 【Mybatis原始碼解析】- 整體架構及原理MyBatis原始碼架構
- Mybatis原始碼分析MyBatis原始碼
- 精盡MyBatis原始碼分析 - MyBatis-Spring 原始碼分析MyBatis原始碼Spring
- 淺析 及整體分析 Relay 原始碼原始碼
- jQuery原始碼分析系列 : 整體架構jQuery原始碼架構
- vue-router原始碼分析-整體流程Vue原始碼
- MyBatis原始碼分析(二)MyBatis原始碼
- (一) Mybatis原始碼分析-解析器模組MyBatis原始碼
- Mybatis原始碼分析--待完成MyBatis原始碼
- MyBatis快取原始碼分析MyBatis快取原始碼
- MyBatis3教程 - MyBatis Interceptor原始碼分析MyBatisS3原始碼
- 【MyBatis原始碼分析】select原始碼分析及小結MyBatis原始碼
- Mybatis 原始碼解讀-設計模式總結MyBatis原始碼設計模式
- Mybatis執行流程原始碼分析MyBatis原始碼
- Mybatis-Plus Generate 原始碼分析MyBatis原始碼
- vue-loader 原始碼解析系列之 整體分析Vue原始碼
- RPC框架整體架構設計分析RPC框架架構
- 精盡MyBatis原始碼分析 - Spring-Boot-Starter 原始碼分析MyBatis原始碼Springboot
- Mybatis的快取——一級快取和原始碼分析MyBatis快取原始碼
- 通過原始碼分析Mybatis的功能原始碼MyBatis
- 從原始碼角度分析 MyBatis 工作原理原始碼MyBatis
- 【MyBatis原始碼分析】環境準備MyBatis原始碼
- tomcat原始碼分析(第一篇 從整體架構開始)Tomcat原始碼架構
- OkHttp 原始碼剖析系列(二)——攔截器整體流程分析HTTP原始碼
- thrift原始碼分析-架構設計原始碼架構
- Kinect體感機器人(一)—— 整體設計機器人
- 精盡 MyBatis 原始碼分析 - MyBatis 初始化(一)之載入 mybatis-config.xmlMyBatis原始碼XML
- MyBatis原始碼分析之核心處理層MyBatis原始碼
- mybatis原理,配置介紹及原始碼分析MyBatis原始碼
- 精盡MyBatis原始碼分析 - 文章導讀MyBatis原始碼