一、参考资料
MyBatis官网
玩转 MyBatis:深度解析与定制
Mybatis介绍
为什么建议框架源码学习从Mybatis开始
二、整体结构
三、配置文件
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获取到全局配置文件的二进制流
- XMLConfigBuilder构建SqlSessionFactory
3.2 Mapper.xml的解析
XMLConfigBuilder#parseConfiguration
mapperElement(root.evalNode("mappers"));
四、缓存
一级缓存基于SqlSession,可以直接创建SqlSessionFactory,并从中开启一个新的SqlSession,默认情况下它会自动开启事务。
一级缓存失效的情景:
- 跨SqlSession的一级缓存不共享
- 两次相同的查询间有DML操作
- 手动清空了一级缓存
SpringFramework/SpringBoot整合MyBatis后,Service方法中没有开启事务时,每次调用Mapper查询数据时,底层都会创建一个全新的SqlSession去查数据库。
BaseExecutor#query
protected PerpetualCache localCache;
- SqlSession关闭时,一级缓存的数据进入二级缓存
- 二级缓存中有数据时,直接取出,不会预先开启Connection
- 二级缓存基于nameSpace级别
CachingExecutor#query
private final TransactionalCacheManager tcm = new TransactionalCacheManager();
public List 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 list = (List) 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:二级缓存应该是基于事务提交的,只有事务提交后,数据库的数据确定没有问题,这个时候SqlSession中的一级缓存数据也是准确的,这样才能把一级缓存的数据写入到二级缓存中。
二级缓存在写入时已经执行了一次基于jdk的序列化动作,每次从二级缓存取数据时,会再执行一次反序列化,将字节数组转为缓存数据对象。
五、日志
Logger增强类-动态代理。
- PreparedStatementLogger
- ConnectionLogger
- ResultSetLogger
六、Spring整合MyBatis
SqlSessionFactoryBean:只负责mapper.xml的处理。
public class SqlSessionFactoryBean
implements FactoryBean, InitializingBean, ApplicationListener {
// ......
private Resource configLocation;
private Configuration configuration;
- 几乎可以代替MyBatis全局配置文件
- 可以传入全局配置文件,供MyBatis解析和处理
- 代替MyBatis处理数据源和事务工厂
- 只处理和解析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,它会执行包扫描的动作,并且将扫描到的Mapper接口都收集起来,构造成MapperFactoryBeanMapper,注入到SqlSessionFactory和SqlSessionTemplate。
七、生命周期
整体结构:
Executor类结构:
- CRUD操作
- 事务控制和获取
- 二级缓存的控制
- 延迟加载
selectList的整体调用时序图:
- SqlSource中传入参数,返回BoundSql的过程,会将动态SQL解析转化为可以执行的带占位符的SQL语句。