为什么要是用缓存
使用缓存可以使应用更快的获取数据,避免频繁的数据库交互,尤其是在查询越多,缓存命中率越高的情况下,使用缓存的作用就很明显。
一、一级缓存
也叫本地缓存。存在与sqlsession的生命周期中,在同一个sqlsession查询时,MyBatis会把执行的方法和参数通过算法生成缓存的键值,将键值和查询结果存储到Map对象中。如果同一个sqlsession中执行的方法和 参数完全相同,那么通过算法会生成完全一致的键值(Map),当map缓存对象中存在该键值时,返回缓存中的对象。
当进行insert、update、delete操作时,MyBatis会清空一级缓存。
存在的问题:假如在一个sqlSession中存在两个完全相同的查询操作,那么按照一级缓存,这两个查询返回的应该是同一个对象。此时如果在第一个查询获取返回值后,对该返回值进行了修改且没有进行持久化,那么第二次查询的结果就会变为修改后的内容,产生脏数据。
如果不希望让查询语句使用以及缓存,可以再mapper文件中对应的select标签中加入flushCache = "true",(但是不推荐)
二、二级缓存
mybatis二级缓存非常强大,它不同于一级缓存只存在于SqlSession的生命周期中,可以理解为存在于SqlSessionFactory的生命周期中。当存在多个SqlSessionFactory时,它们的缓存是绑定在各自的对象上的,缓存数据之间一般不相通。
二级缓存何时刷新?
- 在执行insert,update,delete时自动刷新;
- 在sqlSession执行commit()时刷新;
- 如果sql语句中加入了 flushCache="true",那么无论什么类型的sql执行都会刷新二级缓存;
1、配置二级缓存
在mybatis-config.xml的settings中配置
<!--Mybatis二级缓存开关,默认为true-->
<setting name="cacheEnabled" value="true"/>
Mybatis的二级缓存是和命名空间绑定的,即二级缓存需要配置在*Mapper.xml中或者*Mapper.java接口中。给出这两种方式的配置示例:
*Mapper.xml
<mapper namespace="tk.mybatis.simple.mapper.UserMapper">
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"
/>
<!-- 其他配置 -->
</mapper>
*Mapper.java
@CacheNamespace(
eviction = FifoCache.class,
flushInterval = 60000,
size = 512,
readWrite = true
)
public interface UserMapper {
//接口方法
}
2、作用和配置项
配置二级缓存后会有如下效果:
- 映射文件中的所有select语句将会缓存;
- 映射语句文件中所有INSERT,UPDATE,DELETE语句会刷新缓存;
- 缓存会用Least Recently Used (LRU 最近最少使用的)算法来回收;
- 根据时间表来刷新(如no Flush Interval,没有刷新间隔,即不刷新);
- 缓存会存储集合或对象(无论查询返回什么类型的值)的1024个引用;
- 缓存会被视为read/write(可读/可写)的,意味着对象检索不是共享的,而且可以安全的被调用者修改。而不干扰其他调用者或线程所做的潜在修改。
cache可以配置的属性如下:
1.eviction(收回策略):
- LRU(最近最少使用):默认值,移除最长时间不被使用的对象;
- FIFO(先进先出):按对象进入缓存的顺序来回收;
- SOFT(软应用):移除基于垃圾回收器状态和软应用规则的对象;
- WEAK(弱应用):更积极的移除基于垃圾回收器状态和弱引用规则的对象。
2.flushInterval(刷新间隔):可以被设置为任意的正整数,默认不设置,代表不刷新,缓存仅仅在调用sql语句时刷新;
3.size(引用数目):可以被设置为任意正整数,默认值为1024;
readonly(只读):可以被设置为true或false,只读的缓存会给所有调用者返回相同的缓存对象,因此这些对象不能修改,这提供了很重要的性能优势;可读写的缓存会通过序列化返回缓存对象的拷贝,这种方式会慢一些,但是安全。因此默认是false;
3.参照缓存(最大的作用是解决脏读)
当同事在*Mapper.xml和*Mapper.java中配置二级缓存时,会抛出一个异常:
org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration.Cause : java.lang.IllegalArgumentException: Caches collection already contains value for tk.mybatis.simple.mapper.RoleMapper
这是因为Mapper接口和对应的XML文件是相同的命名空间,想使用二级缓存,二者必须同时配置。这时应该使用参照缓存:
Mapper接口参照XML文件:将mapper接口的二级缓存改为:
/**
* @Author:YR123
* @Date:2019/3/19 14:16
* @Description:
*/
@CacheNamespaceRef(UserMapper.class)
public interface UserMapper {
//接口方法
}
XML文件参照Mapper接口:将XML文件的配置改为:
<mapper namespace="tk.mybatis.simple.mapper.UserMapper">
<cache-ref namespace="tk.mybatis.simple.mapper.UserMapper"/>
<!-- 其他配置 -->
</mapper>
三、集成EhCache缓存入门
EhCache是一个纯粹的java进程内缓存框架,具有快速,精干等特点。有以下特性:
- 快速
- 简单
- 多种缓存策略
- 缓存数据有内存和磁盘两级,无须担心容量问题
- 缓存数据会在虚拟机重启的过程中写入磁盘
- 可以通过RMI,可插入API等方式进行分布式缓存
- 具有缓存和缓存管理器的侦听接口
- 支持多缓存管理器实例以及一个实例的多个缓存区域
1.添加项目依赖
在pom.xml中添加依赖
<!--mybatis集成EhCache-->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.0.3</version>
</dependency>
2.配置EhCache
在src/main/resources目录下新增ehcache.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<diskStore path="D:/cache"/>
<!--默认ehcache缓存配置-->
<defaultCache
maxElementsInMemory="1000"
eternal="false"
copyOnRead="true"
copyOnWrite="true"
timeToIdleSeconds="3600"
timeToLiveSeconds="3600"
overflowToDisk="true"
diskPersistent="true"
/>
<!--匹配RoleMapper.xml中ehcache缓存配置-->
<cache name="tk.mybatis.simple.mapper.RoleMapper"
maxElementsInMemory="100"
eternal="false"
copyOnRead="true"
copyOnWrite="true"
timeToIdleSeconds="3600"
timeToLiveSeconds="3600"
overflowToDisk="true"
diskPersistent="true"
/>
</ehcache>
其中可以看到配置中有一个默认的cache配置和一个指定了name的cache配置,这个name配置会去寻找nameSpace等于此值的RoleMapper.xml配置文件。
- copyOnRead:判断从缓存中读取数据时返回的是对象的引用还是复制一个对象返回。默认为false;
- copyOnWrite:判断写入缓存时是直接缓存对象的引用还是复制一个对象然后缓存,默认为false。
3.修改*Mapper.xml(nameSpace=“xxxxx”的mapper文件)
如果存在cache标签就将其注释掉,然后加入以下内容
<!--集成ehcache缓存配置-->
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
四、集成Redis缓存入门
MyBatis项目开发者提供了Redis的MyBatis二级缓存实现,该项目名为redis-cache,
项目地址为https://github.com/mybatis/redis-cache
1.添加pom依赖
<!--mybatis集成redis缓存-->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-redis</artifactId>
<version>1.0.0-beta2</version>
</dependency>
2.配置redis
简而言之,使用redis缓存需要在本地安装redis服务,参考:Redis学习(一)、windows环境下的redis安装和部署
Redis服务启动后,在src/main/resources目录下新增redis.properties文件
host=localhost
port=6379
connectionTimeout=5000
soTimeout=5000
password=
database=0
clientName=
3.修改*Mapper.xml中的缓存配置
<!--集成redis缓存-->
<cache type="org.mybatis.caches.redis.RedisCache"/>
4.注意的点
redisCache在保存缓存数据和获取缓存数据时,使用了java的序列化和反序列化,因此需要保证被缓存的对象实现java的Serializable接口。