MyBatis 源码分析-技术分享
生活随笔
收集整理的這篇文章主要介紹了
MyBatis 源码分析-技术分享
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
2019獨角獸企業重金招聘Python工程師標準>>>
MyBatis 源碼分析
- MyBatis的主要成員
- Configuration ? ? ? ?
- MyBatis所有的配置信息都保存在Configuration對象之中,
- 配置文件中的大部分配置都會存儲到該類中
- SqlSession ? ? ? ? ? ?
- 作為MyBatis工作的主要頂層API,
- 表示和數據庫交互時的會話,完成必要數據庫增刪改查功能
- Executor ? ? ? ? ? ? ?
- MyBatis執行器,
- 是MyBatis 調度的核心,
- 負責SQL語句的生成和查詢緩存的維護
- StatementHandler
- 封裝了JDBC Statement操作,
- 負責對JDBC statement 的操作,
- 如設置參數等
- ParameterHandler ?
- 負責對用戶傳遞的參數轉換成JDBC Statement 所對應的數據類型
- ResultSetHandler ?
- 負責將JDBC返回的ResultSet結果集對象轉換成List類型的集合
- TypeHandler ? ? ? ? ?
- 負責java數據類型和jdbc數據類型(也可以說是數據表列類型)之間的映射和轉換
- MappedStatement ?
- MappedStatement維護一條<select|update|delete|insert>節點的封裝
- SqlSource ? ? ? ? ? ? ?
- 負責根據用戶傳遞的parameterObject,
- 動態地生成SQL語句,將信息封裝到BoundSql對象中,并返回
- BoundSql ? ? ? ? ? ? ?
- 表示動態生成的SQL語句以及相應的參數信息
- knownMappers.put(type, new MapperProxyFactory<T>(type));
- sqlSessionFactory.openSession();
- openSessionFromDataSource
- ?
- openSessionFromDataSource
- Configuration ? ? ? ?
- spring-mybatis
- ClassPathMapperScanner doScan方法的真正調用地方
- definition.setBeanClass(this.mapperFactoryBean.getClass()); 注冊 beanDefinition class 為mapperFactoryBean
- xmlConfigBuilder
- configuration = xmlConfigBuilder.getConfiguration();
- XMLStatementBuilder
- builderAssistant.addMappedStatement...
- MappedStatement.Builder statementBuilder = new MappedStatement.Builder(...
- builderAssistant.addMappedStatement...
- XMLMapperBuilder 解析xml 配置文件,包括 mapper.xml 的配置
- xmlMapperBuilder.parse();
- bindMapperForNamespace 綁定 mapper
- configuration.addMapper(boundType);
- mapperRegistry.addMapper(type);
- knownMappers.put(type, new MapperProxyFactory<T>(type));
- mapperRegistry.addMapper(type);
- configuration.addMapper(boundType);
- bindMapperForNamespace 綁定 mapper
- xmlMapperBuilder.parse();
- return this.sqlSessionFactoryBuilder.build(configuration);
- return new DefaultSqlSessionFactory(config);
- MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
- public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {if (!this.externalSqlSession) {this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);} }
- SqlSessionTemplate 本質是 SqlSession 的裝飾器
- this.sqlSessionProxy = (SqlSession) newProxyInstance( // sqlSessionProxy 本質是 SqlSession的動態代理new Class[] { SqlSession.class },new SqlSessionInterceptor());
- if (!isEmpty(this.plugins)) {for (Interceptor plugin : this.plugins) { configuration.addInterceptor(plugin); // 初始化插件if (LOGGER.isDebugEnabled()) {LOGGER.debug("Registered plugin: '" + plugin + "'");}}
}
- interceptorChain.addInterceptor(interceptor);
- protected final InterceptorChain interceptorChain = new InterceptorChain(); // InterceptorChain 是configuration 的成員變量
- 插件的調用是在 創建四大 Handler 的時候 執行?pluginAll
- 是在程序執行階段 比如:doUpdate 時候,執行 newParameterHandler
- StatementHandler handler = configuration.newStatementHandler(
- public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);return parameterHandler; }
- StatementHandler handler = configuration.newStatementHandler(
- 是在程序執行階段 比如:doUpdate 時候,執行 newParameterHandler
-
processPropertyPlaceHolders
- 執行屬性的處理,簡單的說,
- 就是把xml中${XXX}中的XXX替換成屬性文件中的相應的值
- 執行屬性的處理,簡單的說,
- findCandidateComponents
- if (isCandidateComponent(sbd)) {
- protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent(); }
- if (isCandidateComponent(sbd)) {
- ClassPathMapperScanner doScan方法的真正調用地方
- 初始化階段:
- spring源碼學習之整合Mybatis原理分析
- 程序運行階段:
- 基本運行:
- public static void main(String[] args) {//定義 SqlSessionFactorySqlSessionFactory sqlSessionFactory = null;try {//使用配置文件創建 SqlSessionFactorysqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis-config.xml"));} catch (IOException ex) {//打印異常.Logger.getLogger(MainCh1.class.getName()).fatal("創建 SqlSessionFactory失敗", ex);return;}//定義 sqlSessionSqlSession sqlSession = null;try {//用sqlSessionFactory創建sqlSessionsqlSession = sqlSessionFactory.openSession();//獲取MapperUserMapper userMapper = sqlSession.getMapper(UserMapper.class);//執行Mapper接口方法.UserPO user = userMapper.findUser(1);//打印信息System.err.println(user.getUsername());} finally {//使用完后要記得關閉sqlSession資源if (sqlSession != null) {sqlSession.close();}}}
- spring 托管:
- 添加配置 <import resource="spring-mybatis.xml"/>
- 基本運行:
- 插件:
- 以分頁插件為例:
- 需要 繼承?Interceptor
- @Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),} ) public class PageInterceptor implements Interceptor {
- 需要 繼承?Interceptor
- interceptorChain.pluginAll(executor) 在configuration 內部注冊所有的 plugin
- 本質就是getSignatureMap 方法,掃描所有注解,對配置的Signature 方法進行 動態代理
- 代理類就是public class Plugin implements InvocationHandler
- 執行Plugin 的invoke 會判斷該方法是否被代理(signatureMap 里面有沒有)
- 如果有執行 intercept 方法
- 該方法最后一行執行的proceed 方法,其實就是該方法的invoke 執行
- 以分頁插件為例:
- 手寫TypeHandler:
- 詳見 催收系統CalendarTypeHandler
- 緩存:
- 一級緩存:
- 一級緩存默認開啟,SqlSession 級別的
- 驗證:
- 相同的查詢,連續查詢兩遍,記錄查詢用時,會發現第二次快得多
- update、 insert、delete 等語句會觸發清除緩存
- 一級緩存,在存在倆sqlsession 時,可能存在臟數據的情況
- 比如,sqlsessionA 兩次相同查詢t 表中間,
- sqlsessionB 更新了t表數據,
- sqlsessionA 第二次查詢的數據就是可能已被修改的臟數據
- 二級緩存:
- 二級緩存默認關閉,SqlSessionFactory 級別的?
- 更不靠譜,開啟方式:
- 一級緩存:
- N+1問題:
- 存在級聯查詢(嵌套查詢)中
- 外層查詢的一條結果數據,由內層查詢獲得
- 外層查詢一次,獲得結果數N ,就要進行N 次內層查詢
- (官方不鼓勵使用,這樣產生大量1+N次查詢)
- 由于1+N 問題的性能損耗,可以考慮配合使用 延時加載
- 官網解釋:
- 存在級聯查詢(嵌套查詢)中
- lazy loading 是怎么做到的?
- 懶加載在級聯查詢時用到了,SimpleStatementHandler 里面query結果
- DefaultResultSetHandler 處理結果
- handleResultSets -->handleResultSets -->...getRowValue-->createResultObject?
- 如果有嵌套查詢且開啟了懶加載?那么會使用代理工廠來處理(代理工廠類型cglib或javasissit類型(默認))
- 針對某一個屬性,當執行
- 新版本,已經變化:
- 解釋為什么是“或”的關系:lazyLoadTriggerMethods 包含該方法的時候,
- 說明對象再進行 equals、clone比較,需要所有屬性全部查詢來才能進行
- protected Set<String> lazyLoadTriggerMethods =
- new HashSet<String>(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" }));
- protected Set<String> lazyLoadTriggerMethods =
- 說明對象再進行 equals、clone比較,需要所有屬性全部查詢來才能進行
- 新版本,已經變化:
- 在嵌套查詢的時候 get/set 方法會觸發 ResultLoaderMap LoadPair load() 方法去查詢(我看源代碼的理解),
- 我找到了觸發函數lazyLoadTriggerMethods 里面沒有get/is?
- 依賴的是PropertyNamer.isGetter(methodName)
- 懶加載在級聯查詢時用到了,SimpleStatementHandler 里面query結果
?
?
?
?
轉載于:https://my.oschina.net/u/3847203/blog/3019311
總結
以上是生活随笔為你收集整理的MyBatis 源码分析-技术分享的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CentOS 7配置IP的几种方法。
- 下一篇: 仓库管理的5S如何在仓库中实施