0、delegate属性即装饰器,调用cache中的任意一个方法都会沿着链条往下依次执行。
1、JDBC的PreparedStatement(一次编译多次执行)每执行一次executeQuery()就会清空上一次的参数,执行完executeQuery()后就可以获取结果。
ReuseExecutor可重用执行器可以指定使用JDBC批处理的statement吗?
2、 ReuseExecutor与BatchExecutor是不是都是使用JDBC的PreparedStatement;只不过BatchExecutor使用PreparedStatement的addBatch方法实现批处理,而ReuseExecutor没有使用该方法?
3、LRU算法为什么要采用LinkHashMap实现?
HashMap 是 Java Collection Framework的重要成员,也是Map族(如下图所示)中我们最为常用的一种。不过遗憾的是,HashMap是无序的,也就是说,迭代HashMap所得到的元素顺序并不是它们最初放置到HashMap的顺序。HashMap的这一缺点往往会造成诸多不便,因为在有些场景中,我们确需要用到一个可以保持插入顺序的Map。庆幸的是,JDK为我们解决了这个问题,它为HashMap提供了一个子类—— LinkedHashMap。虽然LinkedHashMap增加了时间和空间上的开销,但是它通过维护一个额外的双向链表保证了迭代顺序。特别地,该迭代顺序可以是插入顺序,也可以是访问顺序。因此,根据链表中元素的顺序可以将LinkedHashMap分为:保持插入顺序的LinkedHashMap 和保持访问顺序的LinkedHashMap,其中LinkedHashMap的默认实现是按插入顺序排序的。与HashMap相比,LinkedHashMap增加了两个属性用于保证迭代顺序,分别是双向链表头结点header 和 标志位accessOrder(值为true时,表示按照访问顺序迭代;值为false时,表示按照插入顺序迭代)。因此LinkedHashMap可以很好的支持LRU算法,然而当我们要用LinkedHashMap实现LRU算法时,就需要调用相应的构造方法并将accessOrder置为true。
摘自原文链接:https://blog.csdn.net/justloveyou_/article/details/71713781
4、FIFO底层实现:队列。
5、 二级缓存获取同一个缓存,得到的结果一样但是却不是同一个缓存对象,因为使用了序列化。为什么要进行序列化?序列化可以关闭吗?
跨线程不能让两个对象完全一样否则可能会出现数据混乱,所以要有序列化。该对象数据有变化时会重新从数据库中进行查询。
6、一级缓存查过之后马上可以取,二级缓存必须会话提交之后才能获取刚刚设置的缓存值。自动提交也不会命中缓存吗?调用清空方法时也必须要进行提交才会真正生效,否则还是会存在缓存。
7、二级缓存与执行器之间的结构关系
BaseExecutor基础抽象执行器是三个具体执行器共性功能(一级缓存维护、事务管理)的抽象集合(竖向扩展),主要是MyBatis关于一级缓存相关的逻辑。而MyBatis二级缓存是由CachingExecutor二级缓存执行器进行横向扩展,具体是采用装饰者模式进行实现。
CachingExecutor二级缓存执行器包含了一级缓存执行器
@CacheNamespace声明二级缓存空间
二级缓存必须提交之后才会更新
8、会话与执行器之间的关系
9、一次查询请求完整的执行流程
10、如何包装的二级缓存流程
11、二级缓存与一级缓存
二级缓存生命周期与应用同步即应用不挂,二级缓存就不会被gc;
二级缓存在CachingExecutor;一级缓存在BaseExecutor
12、Mybatis一级缓存命中场景:
一级缓存是默认打开的,存储结构为key-value形式底层是HashMap
localCacheScope=STATEMENT在中
13、只有执行数据库查询操作的才会存在StatementHandler
14、为什么要通过Configuration创建StatementHandler?目的就是要统一。(简单工厂);对对象统一插件拦截,嵌入插件
15、为什么要设置一个ResultContext组件?控制解析数量及解析状态。
16、懒加载那里为什么会用Exector执行器而不是会话呢?懒加载只使用一次,而会话是多次,所以直接使用Exector执行器就可以了。
17、源码阅读的一个小技巧
先了解整体分为哪些大的组件,再针对想了解的组件写测试案例,打断点看组件结构;源码中很多时候,参数最多的方法往往就是最终的一个实现即具体逻辑的处理(方法重载、参数的转换与传递)