Mybatis对缓存提供支持,但是在没有配置的默认情况下,它只开启一级缓存,一级缓存只是相对于同一个SqlSession而言。所以在参数和SQL完全一样的情况下,我们使用同一个SqlSession对象调用一个Mapper方法,往往只执行一次SQL,因为使用SelSession第一次查询后,MyBatis会将其放在缓存中,以后再查询的时候,如果没有声明需要刷新,并且缓存没有超时的情况下,SqlSession都会取出当前缓存的数据,而不会再次发送SQL到数据库。
tips:MyBatis一级缓存是默认开启的
写了一个分页查询的功能
测试类
@Test
public void findUserArrays() {
SqlSession sqlSession = MyBatisUtil.createSqlSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
//测试mybatis一级缓存
UserDao userDao1 = sqlSession.getMapper(UserDao.class);
List<User> userList = userDao.findUserArrays(new Integer[]{1, 2}, 1);
//调用两次方法但实际sql只执行一次(同sqlsession情况下且请求的方法和参数一致)
List<User> userList1 = userDao1.findUserArrays(new Integer[]{1, 2}, 1);
for (User user : userList) {
System.out.println(user.toString());
}
MyBatisUtil.closeSqlSession(sqlSession); //释放资源
}
MyBatis的二级缓存是**Application级别(也就是同一个SqlSessionFactory)**的缓存,它可以提高对数据库查询的效率,以提高应用的性能。
tips:MyBatis二级缓存是默认不开启的
SqlSessionFactory层面上的二级缓存默认是不开启的,二级缓存的开席需要进行配置,实现二级缓存的时候,MyBatis要求返回的POJO必须是可序列化的。 也就是要求实现Serializable接口,配置方法很简单,只需要在映射XML文件配置就可以开启缓存了
1、在 MyBatis的配置文件 mybatis-config.xml中加入
<configuration>
<settings>
<setting name="cacheEnabled" value="true"/>
settings>
configuration>
2、在mapper.xml中开启二级缓存,mapper.xml下的sql执行完成会存储到它的缓存区
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
useCache=false,禁用二级缓存
flushCache=true 刷新缓存 ,一般用于insert,update
tips:这里我们可以看到,当设置二级缓存时,策略是控制整个mapper文件的,其实这样是非常不灵活的,我们更希望能够控制每一个业务方法都能够有不同的缓存策略,这样使用起来也是更符合实际开发的需要,所以下面的就是相对来说更好的一种缓存办法
事实上MyBatis自带的缓存还是有着一定缺陷的,而我们使用第三方的ehcache缓存则会更灵活
添加maven依赖
pom.xml
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-cacheartifactId>
dependency>
<dependency>
<groupId>net.sf.ehcachegroupId>
<artifactId>ehcacheartifactId>
dependency>
在项目的resources下新建一个名为ehcache.xml的配置文件,
然后把下面的代码粘过去即可
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<diskStore path="java.io.tmpdir/Tmp_EhCache"/>
<defaultCache eternal="false" maxElementsInMemory="1000" overflowToDisk="true" diskPersistent="true" timeToIdleSeconds="0" timeToLiveSeconds="600" memoryStoreEvictionPolicy="LRU"/>
<cache
name="myCache"
eternal="false"
maxElementsInMemory="200"
overflowToDisk="false"
diskPersistent="true"
timeToIdleSeconds="0"
timeToLiveSeconds="300"
memoryStoreEvictionPolicy="LRU"/>
ehcache>
上面标签可能会爆红,那个先不用管也没问题
@SpringBootApplication
@EnableCaching //开启缓存
public class XXXXApplication {
public static void main(String[] args) {
SpringApplication.run(XXXXApplication.class, args);
}
}
首先需要开启MyBatis的二级缓存
# mybatis相关配置
mybatis:
mapper-locations: classpath:mappers/*.xml
type-aliases-package: com.r.springboot1.pojo # xml映射文件中实体类起别名
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 在控制台打印日志信息
cache-enabled: true # 开启MyBatis的二级缓存
然后在配置中来读取我们刚刚写的缓存文件ehcache.xml
spring:
cache:
ehcache:
config: classpath:ehcache.xml # 读取ehcache.xml缓存策略文件
因为我们是使用注解来实现缓存,所以像原来在dao层映射文件中来配置缓存是没必要的,所以我们需要在Service的实现层来写缓存注解(注解可以是类上也可以是方法上)
tips:如果使用时只写注解而不指定是哪个缓存策略的话,@Cacheable会自动执行默认策略
可以标记在一个方法上,也可以标记在一个类上。当标记在一个方法上时表示该方法是支持缓存的,当标记在一个类上时则表示该类所有的方法都是支持缓存的。对于一个支持缓存的方法,Spring会在其被调用后将其返回值缓存起来,以保证下次利用同样的参数来执行该方法时可以直接从缓存中获取结果,而不需要再次执行该方法。
@Cacheable可以指定三个属性,value、key(使用key是为了让缓存数据之间能够有所区分,这样不会使其混乱)和condition (过滤条件)
@Cacheable("myCache1") //Cache是发生在ehcache.xml中myCache1上的
public User find(Integer id) {
....
}
@Cacheable({"cache1", "cache2"}) //Cache是发生在ehcache.xml中cache1和cache2上的(可以同时设置多个)
public User find(Integer id) {
.....
}
//自定义策略是指我们可以通过Spring的EL表达式来指定我们的key
//#id指参数id作为key(按id区分同一个缓存策略中的不同缓存信息,使用唯一的id则不会使缓存混到一起出现问题)
@Cacheable(value="myCache1", key="#id")
public User find(Integer id) {
...
}
//#p0标识第一个参数作为key(p相当于param,#p0、#p1、#p2...依次代表第一个、第二个和第三个参数,以此类推)
@Cacheable(value="myCache1", key="#p0")
public User find(Integer id) {
.....
}
//#user.user_id表示对象user属性user_id作为key(可以用对象的属性作为key用来区分,当然id还是唯一的字段,使用起来不会使缓存混乱)
@Cacheable(value="myCache1", key="#user.user_id")
public User find(User user) {
.....
}
//也可以混着这样用
@Cacheable(value="myCache1", key="#p0.user_id")
public User find(User user) {
.....
}
示例 | 描述 |
---|---|
root.methodName | 当前方法名 |
root.method.name | 当前方法 |
root.target | 当前被调用的对象 |
root.targetClass | 当前被调用的对象的class |
root.args[0] | 当前方法参数组成的数组 |
root.caches[0].name | 当前被调用的方法使用的Cache |
condition使用
//表示只有当user的id为偶数时才会进行缓存
@Cacheable(value={"users"}, key="#user.id", condition="#user.id%2==0")
public User find(User user) { ... }
key不是什么时候都适合使用
当要显示一些共享的缓存信息时,没有必要使用key(key适用于将缓存信息独立分离开进行缓存的情况)
在一些未知且经常变化的参数上,没有必要使用key(key适用于缓存一些有限的、且唯一的参数字段上,就比如id)
使用的方法实体类必须经过序列化,否则会报错
报错显示所用的实体类为进行序列化,但同时也证明缓存的确是在运行着
给实体类序列化示例
implements Serializable即可
public class Provider implements Serializable {}
然后运行自己的方法后我们可以在控制台看到,虽然多次请求,但因为我们设置了缓存,会发现sql语句只运行了一次,这也说明我们的缓存设置成功
使用@CachePut时我们可以指定的属性跟@Cacheable是一样的
@Cacheable不同的是使用@CachePut标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中
也就是说@Cacheable每次执行前都会先看一下之前是否有缓存,如果有就用原来存在的缓存,而@CachePut每次执行都直接刷新缓存,不会看之前是否存在缓存
清除缓存, 可以指定的属性有value、key、condition、allEntries、beforeInvocation
tips:一般用于增删改上,因为我们定义的缓存策略中可能存在当我们增删改时还没有过缓存时间,所以这时候就会显示还没有改的样子,所以我们为了能够改变后即刻刷新缓存,所以需要用到这个
@CacheEvict(value="myCache",key="#p0.user_id")
public int updUser(SfUser user) throws Exception {
return sfUserMapper.updUser(user);
}
//allEntries是boolean类型,表示是否需要清除缓存中的所有元素。默认为false,表示不需要。当指定了allEntries为true时,Spring Cache将忽略指定的key。有的时候我们需要Cache一下清除所有的元素,这比一个一个清除元素更有效率(false时指执行方法时,我只清除对应id方法的这一条,true是执行方法时,所有的缓存都清掉,当再次执行有@Cacheable的方法时缓存再被再次加入)
@CacheEvict(value="users", allEntries=true)
public void delete(Integer id) {
System.out.println("delete user by id: " + id);
}
tips:如果使用@CacheEvict的时候不设置key,那么它会清除掉当前所有的缓存(当然清楚的是你设置的当前方法中配置的相应缓存策略,并不是说所有的完完全全的缓存全清除了)