向已有的类添加新功能,通过类的关联关系而不是通过继承,更加灵活。
装饰器模式经典实现:
java.io.InputStream
mybatis中使用了装饰器模式的模块:二级缓存
接口:org.apache.ibatis.cache.Cache
org.apache.ibatis.cache.decorators.BlockingCache等其他实现
BlockingCache实现了Cache并且还拥有一个Cache属性,真正做缓存功能的是delegate属性
Cache所有实现类如下(只有PerpetualCache真实实现了Cache缓存功能,其他类都是对该类的封装)
我们可以在mapper文件里配置当前namespace下的二级缓存策略
例如:
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true" blocking="true"/>
二级缓存真实缓存类会在解析mapper时被实例化
org.apache.ibatis.builder.xml.XMLConfigBuilder#mapperElement
↓
org.apache.ibatis.builder.xml.XMLMapperBuilder#parse
↓
org.apache.ibatis.builder.xml.XMLMapperBuilder#configurationElement
↓
org.apache.ibatis.builder.xml.XMLMapperBuilder#cacheElement
↓
org.apache.ibatis.builder.MapperBuilderAssistant#useNewCache
↓
org.apache.ibatis.mapping.CacheBuilder#build
这里会根据配置生成对应的Cache类
public Cache build() {
setDefaultImplementations();
Cache cache = newBaseCacheInstance(implementation, id);
setCacheProperties(cache);
// issue #352, do not apply decorators to custom caches
if (PerpetualCache.class.equals(cache.getClass())) {
for (Class<? extends Cache> decorator : decorators) {
cache = newCacheDecoratorInstance(decorator, cache);
setCacheProperties(cache);
}
cache = setStandardDecorators(cache);
} else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) {
cache = new LoggingCache(cache);
}
return cache;
}
一级缓存与二级缓存
我们可以通过在mybatis主配置文件里配置
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
来开启二级缓存,当然mybatis 是默认开启的;在开启二级缓存后
DefaultSqlSession的executor属性将会实例化成 CachingExecutor类的对象,CachingExecutor对象的delegate才是真正执行查询的执行者,默认情况下是org.apache.ibatis.executor.SimpleExecutor,而SimpleExecutor里的localCache才是一级缓存;整个类结构如下图
所以二级缓存是在一级缓存前生效的;
但是有一个坑就是:
二级缓存是在sqlsession关闭的时候才会将缓存的对象进行持久化(映射的pojo必须要标记java.io.Serializable),所以在同一个sqlsession里连续执行多条相同sql查询那个二级缓存是不会起作用的,这时起作用的时一级缓存;
二级缓存是namespace级别的缓存,如果在不同的namespace里对相同的数据做了修改,那么就有可能出现幻读的错误,所以不建议使用mybatis 的二级缓存;也可以通过cache-ref的方式来让不同的namespace来共享,降低幻读的概率
可以在应用层面上使用redis来做适合业务的逻辑缓存