1, MyBatis的解析
MyBatis的解析工作由XmlConfiguraBuilder类来实现的,它将MyBatis的所有配置信息解析到Configuration中,为之后的使用提供支持。
2, MyBatis的一级缓存
MyBatis的一级缓存是SqlSession级别的缓存,每个SqlSession都有一个以及缓存。
我们在对数据库进行操作时,会创建一个SqlSession,而具体的操作都交给了Executor执行器来实现的,SqlSession只是MyBatis对外提供的接口,Executor内部维护了一个Cache对象,是实现类PerpectualCache实例,Cache对象保存了query查询的cacheKey和查询结果的映射。cacheKey是由statementId、rowbounds、原始sql语句以及参数生成。
当我们执行查询时,首先通过构造cacheKey,去Cache中查询是否缓存了结果,有则直接返回,没有再去数据库中差,然后将结果保存到Cache中再返回。
一级缓存实际上是使用PerpetualCache来维护的,其内部是通过一个HashMap来实现的
3, MyBatis的二级缓存
MyBatis的二级缓存是Application级别的缓存,但是MyBatis对其进行了细化,细化为Mapper级别的缓存,每个Mapper有一个Cache,多个Mapper可以通过cache-ref公用一个Cache。使用MyBatis的二级缓存有三种选择:
1, MyBatis自身提供的缓存实现
2, 用户自定义的Cache接口实现
3, 跟第三方内存缓存库的集成,比如Encache!
开启二级缓存需要三步:
1, MyBatis全局配置文件中将cacheEnable设置为true;
2, Mapper文件中设置cache
3, 具体的select的statement配置中设置useCache为true
MyBatis的二级缓存其实是通过对Executor来实现的。使用二级缓存时,SqlSession创建Executor对象时,会为Executor对象加上一个装饰者:CachingExecutor,其实就是创建一个CachingExecutor对象,它是Executor的装饰者,设计模式之装饰模式自己下去看!当进行查询时,首先CachingExecutor先在二级缓存中查找,找到则直接返回,没有找到再让真正的Executor去执行它的操作(一级缓存和数据库查询),最后CachingExecutor会把Executor的返回结果放到缓存中,然后再返回。
对于MyBatis自身提供的缓存实现,MyBatis定义了大量的Cache的装饰器来增强Cache缓存的功能,对于每个Cache而言,都提供了容量限制,通常采用LRU、FIFO、Scheduled来对缓存进行清除。
4, MyBatis的二级缓存带来的问题
MyBatis的二级缓存是松散的Cache缓存管理和维护,一个Cache对象只能关联到自身Mapper的操作,Mapper之间的缓存关系比较松散,这样会导致一个问题:
A表、B表,A表的查询关联了B表,这条查询结果放到了MapperA的Cache中,B表的update会清除MapperB的Cache,但是MapperA的Cache缓存了B的这些数据,导致B的更新对MapperA不可见,导致数据错误。
解决:
1, A表和B表共用一个Cache,这样可以解决,但是这样会导致缓存的使用效率低,因为两个Mapper的更新操作都会清空Cache,频繁地清空Mapper导致命中率和使用率都变得很低!
2, 某些表执行更新操作后,去清空跟这些表有关联的查询语句造成的缓存。决定清空哪些查询缓存,是在EnhancedCachingManager中维护的:update类型的statementId和select类型的statementId集合的映射!
针对2的解决,可以使用Enhanced-Cache(加强的缓存)插件,Enhanced-Cache插件由两个构建组成:EnhancedCachingExecutor、EnhancedCachingManager。
EnhancedCachingExecutor,其实就是一个针对Executor的拦截器,拦截Executor的query和update操作,
1, 当Executor执行query操作时
1.1 记录下该查询的statementId和CacheKey,将其添加到EnhancedCachingManager中
1.2 记录下该查询的statementId和此statementId所属的Mapper内的Cache缓存对象的引用
2, 当Executor执行update操作时,将update操作的statementId传递给EnhancedCachingManager,让它根据statementId的配置,去清空指定的查询语句所产生的缓存。
EnhancedCachingManager,它维护着一下几样东西:
1, 整个MyBatis的所有查询所产生的CacheKey集合
2, 所有statementId及其对应的Cache缓存对象的引用
3, update类型的statementId和select类型的statementId集合的映射,用于当执行update操作时,根据此映射决定清空的查询缓存。