开源框架是如何使用设计模式的-MyBatis缓存机制之装饰者模式

这里就不了它的概念了,总结下就是套娃。利用组合的方式将装饰器组合进来,增强共同的抽象方法(与代理很类似但是又更灵活)

MyBatis缓存 回忆下传统手艺 <!-- 先进先出,60秒刷新一次,可存储512个引用,返回对象只读,不同线程中的调用者之间修改会导致冲突 --> <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/> 粗略回顾下MyBatis缓存 一级缓存

MyBatis的一级缓存存在于SqlSession的生命周期中,在同一个SqlSession中查询时,MyBatis会把执行的方法和参数通过算法生成缓存的键值,将键值和查询结果存入一个Map对象中。如果同一个SqlSession中执行的方法和参数完全一致,那么通过算法会生成相同键值,当Map缓存对象中已经存在该键值时,则会返回缓存中的对象。

默认开启

二级缓存

MyBatis的二级缓存非常强大,它不同于一级缓存只存在于SqlSession的生命周期中,而是可以理解为存在于SqlSessionFactory的生命周期中。

默认不开启,需要如下配置后开启全局配置,再在对应的Mapper.xml中添加“传统手艺”-标签

<settings> <setting name = "cacheEnabled" value="true"/> </settings> <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>

另一种开启方式-注解

@CacheNamespace( eviction = FifoCache.class, flushInterval = 60000, size = 512, readWrite = true ) public interface RoleMapper { // 接口方法 }

eviction(收回策略)

LRU(最近最少使用的):移除长时间不使用的对象,这是默认值

FIFO(先进先出):按对象进入缓存的顺序来移除它们

SOFT(软引用):移除基于垃圾回收器状态和软引用规则的对象

WEAK(弱引用):更积极地移除基于垃圾收集器状态和弱引用规则的对象

flushInterval(刷新间隔)

size(引用数目)

readOnly(只读)只读的缓存会给所有调用者返回缓存的相同实例,因此这些对象不能被修改,这提供了很重要的性能优势。可读写的缓存会通过序列化返回缓存对象的拷贝,这种方式会慢一些,但是安全,因此默认是false

集成第三方缓存

MyBatis还支持通过“type”来集成第三方缓存,如下就是集成了Redis缓存,这样就从本地缓存跳跃到了分布式缓存了。

<mapper namespace="xxx.xxx.xxx.mapper.RoleMapper"> <!-- 集成Redis缓存--> <cache type="org.mybatis.caches.redis.RedisCache" /> </mapper> 二级缓存的问题-脏数据

二级缓存虽然能提高应用效率,减轻数据库服务器的压力,但是如果使用不当,很容易产生脏数据

MyBatis的二级缓存是和命名空间绑定的,所以通常情况下每一个Mapper映射文件都拥有自己的二级缓存,不同Mapper的二级缓存互不影响。在常见的数据库操作中,多表联合查询非常常见,由于关系型数据库的设计,使得很多时候需要关联多个表才能获得想要的数据。在关联多表查询时肯定会将查询放到某个命名空间下的映射文件中,这样一个多表的查询就会缓存在该命名空间的二级缓存中。涉及这些表的增删改操作通常不在一个映射文件中,它们的命名空间不同,因此当有数据变化时,多表查询的缓存未必会被清空,这种情况下就会产生脏数据。

基于MyBatis缓存机制结合源码解析装饰器模式

Cache接口:

Cache接口

Cache核心方法:

putObject

getObject

removeObject

DEMO-实战使用MyBatis的装饰者模式 public static void main(String[] args) { final String cacheKey = "cache"; final Cache cache = new LoggingCache(new BlockingCache(new PerpetualCache(cacheKey))); Object cacheValue = cache.getObject(cacheKey); if (Objects.isNull(cacheValue)) { log.debug("缓存未命中 >>>>>>>>> key:[{}]", cacheKey); cache.putObject(cacheKey, "MyCacheValue"); } cacheValue = cache.getObject(cacheKey); log.debug("缓存命中 >>>>>>>>> key:[{}],value:[{}]", cacheKey, cacheValue); }

如代码所示,是不是看到了“装饰者模式”的影子了,在构造函数中疯狂套娃。使用的是MyBatis的API,给基本缓存组件装饰了“日志打印”、“阻塞“的能力。
结果演示:

缓存Demo结果演示


可以看到,LogginCache在读缓存的时候还会打印出缓存命中率。 好了,接下来进入正题,看看其他缓存是怎么实现的吧。以下源码基于MyBatis3.4.5

PerpetualCache private final Map<Object, Object> cache = new HashMap<>(); @Override public void putObject(Object key, Object value) { cache.put(key, value); } @Override public Object getObject(Object key) { return cache.get(key); } @Override public Object removeObject(Object key) { return cache.remove(key); }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zgdjjj.html