Mybatis体系结构与工作原理
宏观的架构与微观的原理
工作流程
Configuration
SqlsessionFactory
Sqlsession
Executor
StatementHandler
https://www.processon.com/view/link/604030a36376893122d8c05e
架构分层与模块划分
接口层
核心层
与数据库操作的相关动作都是在这里层完成的。
主要处理:
1、把接口传入的参数解析并且映射成JDBC类型
2、解析xml文件中的SQL语句(包括插入参数、动态SQL生成)
3、执行SQL语句
4、处理结果集,并映射为Java对象
基础层
主要是抽取一些通过的功能,来支持核心层的功能。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GHx2eU5S-1614819366646)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210303200411999.png)]
缓存详解
目的
提升查询的效率和减少数据库的压力。
体系结构
基本缓存
默认的基础使用缓存
PerpetualCache
淘汰算法缓存
LruCache
FifoCache
SoftCache
WeakCache
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wrGipxYK-1614819366648)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210303201243300.png)]
装饰器缓存
LoggingCache
SynchronizedCache
BlockingCache
ScheduledCache
TransactionalCache
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6MPanb3N-1614819366660)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210303201534846.png)]
一级缓存
又名本地缓存,一级缓存是会话级别的。默认是开启状态,localCacheScope=STATEMENT(关闭一级缓存)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zFNuXNVQ-1614819366664)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210303201941503.png)]
只有在相同会话里,多次执行相同语句,会从内存中取到缓存结果,不会去查询数据库。
1、如何命中一级缓存?CacheKey怎么构成?什么时候会清除?
1、同一会话里,执行相同语句,会命中缓存;
2、CacheKey是由statementId、SQL语句、分页参数、传入参数组成,存在PerpetualCache的Map中;
3、同一会话里,执行更新语句,会清除所有一级缓存。或者select标签中flushCache=true;
2、如果跨会话场景,一级缓存会导致什么问题?
会话A读取了一级缓存数据,会话B修改该条数据。会话A不会感知,依然会从一级缓存中取,拿到过时数据。
一级缓存不能跨会话共享。
总结:如果在多个会话或分布式环境下,会有查到过时数据的问题
二级缓存
解决一级缓存不能跨会话共享的问题,范围是 namesapce 级别,可以被多个Sqlsession共享。
应用端必须要开启 cacheEnabled=true,Mapper.xml显示配置<cache/>标签,才能使用二级缓存生效。
如果只配置了cacheEnabled=true,会对执行器进行包装为CachingExecutor;<cache/>配置决定是否从缓存查询、存储;select标签上的 useCache 属性决定是否使用缓存。
1、为什么事务不提交,二级缓存不生效
二级缓存使用 TransactionalCacheManager (TCM) 来管理,调用了 TransactionalCache 的getObject\putObject\commit方法。
而TransactionalCache 里又持有了真正的 Cache对象,比较经过层层装饰的PerpetualCache。
2、为什么增删改操作会清空二级缓存
在update时,会将二级缓存队列清;commit提交时,会将二级缓存清空
public int update(MappedStatement ms, Object parameterObject) throws SQLException {
flushCacheIfRequired(ms);
return delegate.update(ms, parameterObject);
}
private void flushCacheIfRequired(MappedStatement ms) {
Cache cache = ms.getCache();
if (cache != null && ms.isFlushCacheRequired()) {
tcm.clear(cache);
}
}
3、什么场景下开启二级缓存?
适合在查询为主的场景,比如历史交易、历史订单的查询。如果增删改比较频繁,二级缓存也就失去了意义
4、如何让多个Mapper共享一个二级缓存?
跨 namesapce 的缓存共享问题,可以使用 来解决
<cache-ref namesapce="com.gupaoedu.mapper.BlogMapper"/>
源码解读
private SqlSessionFactory sqlSessionFactory;
@Before
public void prepare() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
/**
* 通过 SqlSession.getMapper(XXXMapper.class) 接口方式
* @throws IOException
*/
@Test
public void testSelect() throws IOException {
SqlSession session = sqlSessionFactory.openSession();
try {
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlogById(1);
session.commit();
mapper.selectBlogById(1);
System.out.println(blog);
} finally {
session.close();
}
}
SqlSessionFactoryBuild 执行类图
https://www.processon.com/view/link/60402d9d5653bb6efa49f324
SqlSessionFactory 执行类图
https://www.processon.com/view/link/60402dd807912951ff2f3b46
SqlSession
https://www.processon.com/view/link/60402df8f346fb55c9ad7be4