一、參考資料
MyBatis官網(wǎng)
玩轉(zhuǎn) MyBatis:深度解析與定制
Mybatis介紹
為什么建議框架源碼學(xué)習(xí)從Mybatis開始
二、整體結(jié)構(gòu)

三、配置文件
https://mybatis.org/mybatis-3/zh/configuration.html#settings
3.1 配置文件加載
InputStream xml = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(xml);
- 通過ClassLoader獲取到全局配置文件的二進(jìn)制流
- XMLConfigBuilder構(gòu)建SqlSessionFactory

3.2 Mapper.xml的解析
XMLConfigBuilder#parseConfiguration
mapperElement(root.evalNode("mappers"));

四、緩存
??一級(jí)緩存基于SqlSession,可以直接創(chuàng)建SqlSessionFactory,并從中開啟一個(gè)新的SqlSession,默認(rèn)情況下它會(huì)自動(dòng)開啟事務(wù)。
??一級(jí)緩存失效的情景:
- 跨SqlSession的一級(jí)緩存不共享
- 兩次相同的查詢間有DML操作
- 手動(dòng)清空了一級(jí)緩存
??SpringFramework/SpringBoot整合MyBatis后,Service方法中沒有開啟事務(wù)時(shí),每次調(diào)用Mapper查詢數(shù)據(jù)時(shí),底層都會(huì)創(chuàng)建一個(gè)全新的SqlSession去查數(shù)據(jù)庫(kù)。
BaseExecutor#query
protected PerpetualCache localCache;
- SqlSession關(guān)閉時(shí),一級(jí)緩存的數(shù)據(jù)進(jìn)入二級(jí)緩存
- 二級(jí)緩存中有數(shù)據(jù)時(shí),直接取出,不會(huì)預(yù)先開啟Connection
- 二級(jí)緩存基于nameSpace級(jí)別
CachingExecutor#query
private final TransactionalCacheManager tcm = new TransactionalCacheManager();
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);
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
?? TransactionalCacheManager:二級(jí)緩存應(yīng)該是基于事務(wù)提交的,只有事務(wù)提交后,數(shù)據(jù)庫(kù)的數(shù)據(jù)確定沒有問題,這個(gè)時(shí)候SqlSession中的一級(jí)緩存數(shù)據(jù)也是準(zhǔn)確的,這樣才能把一級(jí)緩存的數(shù)據(jù)寫入到二級(jí)緩存中。
?? 二級(jí)緩存在寫入時(shí)已經(jīng)執(zhí)行了一次基于jdk的序列化動(dòng)作,每次從二級(jí)緩存取數(shù)據(jù)時(shí),會(huì)再執(zhí)行一次反序列化,將字節(jié)數(shù)組轉(zhuǎn)為緩存數(shù)據(jù)對(duì)象。
五、日志
??Logger增強(qiáng)類-動(dòng)態(tài)代理。
- PreparedStatementLogger
- ConnectionLogger
- ResultSetLogger
六、Spring整合MyBatis
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!-- MyBatis全局配置文件 -->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="typeAliasesPackage" value="com.linkedbear.mybatis.entity"/>
</bean>
??SqlSessionFactoryBean:只負(fù)責(zé)mapper.xml的處理。
public class SqlSessionFactoryBean
implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
// ......
private Resource configLocation;
private Configuration configuration;
- 幾乎可以代替MyBatis全局配置文件
- 可以傳入全局配置文件,供MyBatis解析和處理
- 代替MyBatis處理數(shù)據(jù)源和事務(wù)工廠
- 只處理和解析mapper.xml
??MapperScannerConfigurer:掃描Mapper接口。
public class MapperScannerConfigurer
implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
private SqlSessionFactory sqlSessionFactory;
private SqlSessionTemplate sqlSessionTemplate;
private String sqlSessionFactoryBeanName;
private String sqlSessionTemplateBeanName;
??ClassPathMapperScanner,它會(huì)執(zhí)行包掃描的動(dòng)作,并且將掃描到的Mapper接口都收集起來(lái),構(gòu)造成MapperFactoryBeanMapper,注入到SqlSessionFactory和SqlSessionTemplate。
七、生命周期
??整體結(jié)構(gòu):

??Executor類結(jié)構(gòu):

- CRUD操作
- 事務(wù)控制和獲取
- 二級(jí)緩存的控制
- 延遲加載
??selectList的整體調(diào)用時(shí)序圖:

- SqlSource中傳入?yún)?shù),返回BoundSql的過程,會(huì)將動(dòng)態(tài)SQL解析轉(zhuǎn)化為可以執(zhí)行的帶占位符的SQL語(yǔ)句。
