目录
Mybatis一级缓存、二级缓存
Mybatis缓存
MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。MyBatis 3中的缓存实现的很多改进都已经实现了,使得它更加强大而且易于配置。
默认情况下是没有开启缓存的,除了局部的session缓存,可以增强变现而且处理循环依赖也是必须的。
mybatis提供查询缓存,如果缓存中有数据,就不用从数据库中获取,用于减轻数据压力,提高系统性能。
注意:
在第二次查询之前,对数据库进行增的操作
(1)一级缓存必须提交才会清空缓存
(2)二级缓存不管有没有提交都会清空缓存
一级缓存(sqlSession级别)
一级缓存是sqlSession级别的缓存,在操作数据库的时候,需要构造sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的SqlSession的缓存区域(HashMap)是互相不受影响的,mybatis默认支持一级缓存
测试:连续执行两次查询即可。
什么时候一级缓存会被清空呢?
通过测试发现,当在第二次查询数据库之前对数据库进行了增的操作后,第二次查询就不会从缓存中查询结果,而是直接从数据中查询结果。
另一种就是在第一查询结果后,手动的方式清空一级缓存(使用sqlSession.clearCache();方法)
二级缓存(Mapper级别)
二级缓存是Mapper级别的缓存。多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。(二级缓存Mybatis默认是关闭的,需要自己去手动配置开启或可以自己选择用哪个厂家的缓存来作为二级缓存)
1.开启二级缓存
(1)首先在全局配置文件中配置开启二级缓存功能:
<!-- 开启二级缓存总开关 -->
<setting name="cacheEnabled" value="true"/>
(2)在映射文件中去选择是否该映射文件使用二级缓存:
如果使用就进行以下配置,如果不是用,就不需要对映射文件做任何修改。
<!-- 开启本mapper下的namespace的二级缓存,默认使用的是mybatis提供的PerpetualCache -->
<cache eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true" />
这个更高级的配置创建了一个 FIFO 缓存,并每隔60秒刷新,存储数据结果对象或列表的512个引用,而且返回的对象被认为是只读的,因此在不同线程中的调用者之间修改它们会导致冲突。
eviction(回收策略)默认的是 LRU
回收策略 | 描述 |
---|---|
LRU | 最近最少使用的:移除最长时间不被使用的对象。 |
FIFO | 先进先出:按对象进入缓存的顺序来移除它们。 |
SOFT | 软引用:移除基于垃圾回收器状态和软引用规则的对象。 |
WEAK | 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。 |
flushInterval(刷新间隔)
可以被设置为任意的正整数,而且它们代表一个合理的毫秒 形式的时间段。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。
size(引用数目)
可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的 可用内存资源数目。默认值是 1024。
readOnly(只读)
属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓 存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。可读写的缓存 会返回缓存对象的拷贝(通过序列化) 。这会慢一些,但是安全,因此默认是 false。
(3)也可以设置选择二级缓存提工商
以下是我选择使用EhcacheCache缓存(注意:在使用EhcacheCache缓存需要导入jar包)
需导入一个jar包:ehcache-core-版本.jar
代码省略……
效果如下:
(1)映射语句文件中的所有select语句将会被缓存。
(2)映射语句文件中的所有insert,update和delete语句会刷新缓存。
(3)缓存会使用 Least Recently Used(LRU,最近最少使用的)算法来收回。
(4)根据时间表(比如 no Flush Interval,没有刷新间隔), 缓存不会以任何时间顺序 来刷新。
(5)缓存会存储列表集合或对象(无论查询方法返回什么)的 1024 个引用。
(6)缓存会被视为是 read/write(可读/可写)的缓存,意味着对象检索不是共享的,而 且可以 安全地被调用者修改,而不干 扰其他调用者或线程所做的潜在修改
最后就是,在需要缓存的pojo类中去实现序列化:
public class User implements Serializable{
private Integer id;
private String name;
private int age;
private String address;
}
禁用二级缓存(useCache=false)
由于二级缓存默认是关闭的,如果不开启就不会使用二级缓存。如果,开启了二级缓存,而在有些查询结果集中,不需要受到二级缓存影响,该怎么去做呢?
在select查询中,默认是使用了二级缓存,如果不想使用二级缓存,就在select标签中有一个useCache的属性设置为false,就代表不使用二级缓存,每次进行查询数据都不会从缓存总获取,而是直接从数据库中进行查询。useCache的默认值是true,即代表statement使用二级缓存
刷新二级缓存(flushCache=true)
在statement中设置flushCache=true,可以刷新二级缓存。默认情况下,select语句中的flushCache是false。如果是insert、update、delete语句,那么flushCache的默认值是true。如果将select语句中的flushCache值改为true,就意味着查询语句的二级缓存失效,每次查询都会从数据库进行查询。如果将select语句的flushCache值为false,就代表该查询语句使用了二级缓存,如果在数据库中修改了数据,而二级缓存中的数据还是原来的数据,那么这样就会出现脏读
第三方缓存整合
EhCache是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider(缓存)。
MyBatis定义了Cache接口方便进行自定义扩展
1.导入ehcache包。以及整合包、日志包
ehcache-core-2.6.8.jar
mybatis-ehcache-1.0.3.jar
slf4j-api-1.6.1.jar
slf4j-log4j12-1.6.2.jar
2.编写ehcache.xml配置文件
3.配置cache标签
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
4.参照缓存
若想在命名空间中共享相同的缓存配置和实例,可以使用cache-ref元素来引用另外一个缓存
使用自定义缓存
除了这些自定义缓存的方式,也可以通过实现自己的缓存或为其他第三方缓存方案,创建适配器来完全覆盖缓存行为
<cache type="com.domain.MyCustomCache"/>
这个示例展示了如何使用一个自定义的缓存实现。type属性指定的类必须实现 。
org.mybatis.cache.Cache接口。这个接口是MyBatis框架中很多复杂的接口之一,但是简单给定它做什么就行。
public interface Cache {
String getId();
int getSize();
void putObject(Object key, Object value);
Object getObject(Object key);
boolean hasKey(Object key);
Object removeObject(Object key);
void clear();
}
要配置缓存, 简单和公有的JavaBean属性来配置缓存实现,而且是通过cache元素来传递属性,比如, 下面代码会在缓存实现中调用一个称为 "setCacheFile(String file)"的方法。
<cache type="com.domain.MyCustomCache">
<property name="cacheFile" value="/tmp/my-custom-cache.tmp" />
</cache>
可以使用所有简单类型作为JavaBeans 的属性,MyBatis会进行转换。
记得缓存配置和缓存实例是绑定在SQL映射文件的命名空间是很重要的。因此,所有在相同命名空间的语句正如绑定的缓存一样。语句可以修改和缓存交互的方式,,或在语句的基础上使用两种简单的属性来完全排除它们。默认情况下,语句可以这样来配置
<select flushCache="false" useCache="true" />
<insert flushCache="true" />
<update flushCache="true" />
<delete flushCache="true" />
因为那些是默认的,明显不能明确地以这种方式来配置一条语句。相反。如果想改变默认的行为,只能设置flushCache和useCache 属性。比如。在一些情况下也许想排除从缓存中查询特定语句结果。或者也许想要一个查询语句来刷新缓存。相似地。也许有一些更新语句依靠执行而不需要刷新缓存
参照缓存
这个特殊命名空间的唯一缓存会被使用或者刷新相同命名空间内的语句。也许将来的某个时候,会想在命名空间中共享相同的缓存配置和实例。在这样的情况下,可以使用cache-ref元素来引用另外一个缓存
<cache-ref namespace="com.someone.application.data.SomeMapper"/>