Mybatis框架及原理分析
框架主要類層次結構
1、Mybatis主要做的工作:
1、根據JDBC規範建立與資料庫的連線
2、通過反射打通Java物件與資料庫引數互動之間相互轉化關係
2、主要類的層次關係
SqlSessionFactoryBuilder類通過各種構造器方法,將所有的配置資訊維持在Configuration類中,返回一個SqlSessionFactory;SqlSessionFactory中的openSession方法會產生一個SqlSession的會話,SqlSession這邊就到了CRUD操作了,然而具體的執行者卻是實現了Executor介面的各種類。具體的初始化過程在下面的4.1裡會詳訴
3、主要的類
- SqlSession:作為mybatis工作的主要頂層API,表示和資料庫互動的會話,完成必要資料庫增刪改查功能
- Executor:mybatis執行器,是mybatis排程的核心,賦值SQL語句的生成和查詢快取的維護
- StatementHandler:封裝了JDBC Statement操作,負責對JDBC statement的操作,如設定引數、將Statement結果集轉換成list集合
- ParameterHandler:負責對使用者傳遞的引數轉換成JDBC Statement所需要的引數
- ResultSetHandler:負責將JDBC返回的ResultSet結果集物件轉換成List型別的集合
- TypeHandler:負責java資料型別和jdbc資料型別之間的對映和轉換
- MappedStatement:維護了一條< select|update|delete|insert>節點的封裝
- SqlSource:負責根據使用者傳遞的parameterObject,動態地生成SQL語句, 將資訊封裝到BoundSql物件中,並返回
- BoundSql:表示動態生成的SQL語句以及相應的引數資訊
- Configuration:MyBatis所有的配置資訊都維持在Configuration物件之中
4、執行過程
在別人的部落格的裡看到一張圖,畫的很好直接拿來用了,雖然叫層次圖,但是應該叫執行過程更合適一點:
4.1、初始化
Mybatis的初始化的過程就是解析配置檔案和初始化Configuration的過程,通過流載入mybatis的配置檔案(其中載入關聯的對映檔案),然後SqlSessionFactoryBuilder類構建SQLSession的工廠,即建立SqlSessionFactory建造者物件(這裡用到的是建造者模式)。
在這個過程中,XMLConfigBuilder物件會對XML配置檔案進行解析,依次解析properties/settings/../mappers等節點配置,在解析environments節點時,會根據transactionManager的配置來建立事務管理器,根據DataSource的配置來建立DataSource物件,這裡麵包含了資料庫登陸的相關資訊。
在解析mapper節點時,會讀取該節點下所有的mapper檔案,然後進行解析,並將解析後的結果存到configuration物件中,生成Configuration物件,然後根據Configuration物件來建立SqlSession,到這裡時,mybatis的初始化就完成了。
4.2、Mybatis的Sql查詢流程
前面我們概括Mybatis主要乾的事情就是兩件:根據JDBC規範建立與資料庫的連線和通過反射打通Java物件與資料庫引數互動之間相互轉化關係,下面就來解析具體的查詢流程。
查詢的API介面是在SqlSession中宣告的,建立SqlSession的過程其實就是根據Configuration中的配置來建立對應的類,然後呼叫SqlSession中selectOne方法進行SQL查詢,selectOne方法最後呼叫的是selectList,selectList查詢configuration中儲存的MappedStatement物件,mapper檔案sql語句配置對應MappedStatement
物件,呼叫執行器進行查詢操作。
執行器在query操作中,優先會查詢快取是否命中,命中則直接返回,否則從資料庫中查詢。真正的doQuery操作是有SimplyExecutor代理來完成的,該方法中有2個子流程,一個是SQL引數的設定,另一個是SQL查詢操作和結果集的封裝。
4.2.1、SQL查詢引數的設定
獲取和資料庫connection的連線,然後準備Statement,然後就是設定SQL查詢中的引數值。
// SimpleExecutor類
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;
}
// DefaultParameterHandler類
public void setParameters(PreparedStatement ps) {
......
}
4.2.1、SQL查詢結果集的封裝
ResultSetWrapper是ResultSet的包裝類,呼叫getFirstResultSet方法獲取第一個ResultSet,同時獲取資料庫的MetaData資料,包括資料庫列名、列的型別、類序號等,這些資訊都儲存在ResultSetWrapper類中了,然後呼叫handleResultSet方法來進行結果集的封裝。
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
try {
if (parentMapping != null) {
handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
} else {
if (resultHandler == null) {
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
multipleResults.add(defaultResultHandler.getResultList());
} else {
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
}
}
} finally {
closeResultSet(rsw.getResultSet());
}
}
結果值的設定在方法handleRowValues中。
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
if (resultMap.hasNestedResultMaps()) {
ensureNoRowBounds();
checkResultHandler();
handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
} else {
// 封裝資料
handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
}
}
然後會將表中欄位的型別和java中型別一一對應起來,並最後通過java類中對應的set方法,封裝java類結果集。