二级缓存
在 MyBatis 的全局配置中有一个参数 cacheEnabled,这个参数是二级缓存的全局开关,默认值是 true,初始状态为启用状态。如果把这个参数设置为 false,即使有后面的二级缓存配置,也不会生效。
MyBatis 的二级缓存是和命名空间绑定的,即二级缓存需要配置在 Mapper.xml 映射文件中,或者配置在 Mapper.java 接口中。
在映射文件中,命名空间就是 XML 根节点 mapper 的 namespace 属性。
在 Mapper 接口中,命名空间就是接口的全限定名称。
在 Mapper.xml 中配置二级缓存
在保证二级缓存的全局配置开启的情况下,给 CountryMapper.xml 开启二级缓存只需要在CountryMapper.xml 中添加 <cache/> 元素即可:
cache 可以配置的属性如下:
eviction(回收策略)
LRU(最近最少使用):移除最长时间不被使用的对象,这是默认值。
FIFO(先进先出):按对象进入缓存的顺序来移除它们。
SOFT(软引用):移除基于垃圾回收器状态和软引用规则的对象。
WEAK(弱引用):更积极地移除基于垃圾收集器状态和弱引用规则的对象。
flushInterval(刷新间隔):可以被设置为任意的正整数,代表一个合理的毫秒形式的时间段。默认情况不设置,即没有刷新间隔,缓存仅仅在调用语句时刷新。
size(缓存大小):可以被设置为任意正整数。默认值是 1024,表示缓存会存储 1024 个缓存对象。
readOnly(只读):可以被设置为 true 或 false。
只读的缓存会给所有调用者返回缓存对象的相同实例,因此这些对象不能被修改,这提供了很重要的性能优势。
可读写的缓存会通过序列化返回缓存对象的拷贝,这种方式会慢一些,但是安全,因此默认是 false。
因为可读写的缓存会通过 Java 的序列化机制返回缓存对象的拷贝,因此,缓存对象必须实现 Serilizable 接口。
在 Mapper 接口中配置二级缓存
当只使用注解方式配置二级缓存时,如果在 CountryMapper 接口中,则需要增加如下配置:
只需要增加 @CacheNamespace 注解即可,该注解同样可以配置各项属性:
eviction(回收策略)
flushInterval(刷新间隔)
size(缓存大小)
readWrite(读写)
@CacheNamespace 注解中的各属性与标签中的属性功能类似,除了 readWrite 与 readOnly 这对反义词外,其他属性的用法完全一致。
同时在 Mapper.xml 和 Mapper 接口配置二级缓存
当同时使用注解方式和 XML 映射文件时,如果同时配置了上述的二级缓存,就会抛出如下异常:
这个时候应该使用参照缓存。
在 Mapper 接口中,参照缓存配置如下:
或者,在 Mapper.xml 中使用配置参照缓存。
MyBatis 中很少会同时使用 Mapper 接口注解方式和 XML 映射文件,所以参照缓存并不是为了解决这个问题而设计的。参照缓存除了能够通过引用其他缓存减少配置外,主要的作用是解决脏读。
示例
上面给出了两个示例,程序的逻辑完全一样,但是对于 country2 的 name 值却完全不同。
造成这种区别的原因在于:当调用 close 方法关闭 SqlSession 时,SqlSession 才会保存查询数据到二级缓存中。因此,第一个测试程序会在执行完
country1.setName("NEW NAME");
之后才将 country1 保存到二级缓存中,而第二个测试程序则在countryMapper.selectByPrimaryKey(1)
执行完后立刻便将 country1 保存到了二级缓存中。通过上面两个示例程序,可以发现,进入二级缓存的是原始对象的一个拷贝,并不会造成指针泄露。
MyBatis 默认提供的缓存实现是基于 Map 实现的内存缓存,已经可以满足基本的应用。但是当需要缓存大量的数据时,不仅仅能通过提高内存来使用 MyBatis 的二级缓存,还可以选择一些类似 EhCache 的缓存框架或 Redis 缓存数据库等工具来保存 MyBatis 的二级缓存数据。
Last updated