在MyBatis中,存在一级缓存以及二级缓存。一级缓存是默认自动开启,而二级缓存需要我们手动去开启。但看到这篇文章的人,大部分都是在做项目才发现的问题:每次访问数据都要查询一遍又一遍的数据库,这是在控制台上可以发现的。导致数据库压力很大(虽然一个人是看不出来的),但要是众多人使用的就会导致数据库压力大,这点可想而知。
这时候就需要启动MyBatis自带的二级缓存了。
使用二级缓存需要具备以下几个条件:
1、在核心配置文件添加cache-enabled: true(全局性地开启或关闭所以映射器配置文件已配置的任何缓存)
但这是默认开启的,所以可以不用添加
2、在需要的mapper映射文件中的<mapper></mapper>里添加<cache />
3、使用二级缓存的实体类对象必须是可序化的,也就是必须实现java.io.Serializable接口
4、纯MyBatis中需要将SqlSession对象关闭或提交之后,一级缓存才会被写入二级缓存中,此时二级缓存才可用
总而言之就是两步:1、所有的Mapper.xml文件配置<cache/>;2、所有实体类继承序列化接口。
这样子二级缓存就开启了。每次查询同一条Sql就只会返回一次查询记录。
那么问题又来了,为什么我们已经做到这个地步了还需要使用Redis这个NoSQL呢?虽然MyBatis的二级缓存已经挺好用了,但在分布式集群环境下,应用部署在不同的服务器当中,根据负载均衡的原理,查询的操作可以会访问不同的服务器,如果其中的一个服务器断电什么的,那么缓存就不存在了,就又需要再次查询数据库。所以说需要使用分布式缓存来解决这个问题。
第一步:导入坐标
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
第二步:在yml配置中使用Redis相关配置
spring:
#Redis相关配置
redis:
host: localhost
#password:
port: 6379
database: 0 #操作的是0号数据库
#Redis连接池配置
pool:
max-active: 8 #最大连接数
max-wait: -1 #连接池最大阻塞等待时间 -1表示无限
max-idle: 4 #连接池中的最大空闲连接
min-idle: 0 #连接池中的最小空闲连接
第三步:MyBatis的缓存机制是通过其内部有个PerpetualCache类去实现Cache从而实现缓存的。那么我们同样可以写一个与其相同的类去存储。
import org.apache.ibatis.cache.Cache;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
public class RedisCache implements Cache {
//id指定当前放入缓存的mapper的namespace
private final String id;
private final RedisTemplate redisTemplate;
public RedisCache(String id) {
//获取Redis实例
redisTemplate = ApplicationContextUtil.getBean("redisTemplate",RedisTemplate.class);
//指定key的序列化方式
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
this.id = id;
}
@Override
public String getId() {
return id;
}
@Override
public void putObject(Object key, Object value) {
redisTemplate.opsForHash().put(id,key.toString(),value);
}
@Override
public Object getObject(Object key) {
return redisTemplate.opsForHash().get(id,key.toString());
}
@Override
public Object removeObject(Object key) {
return redisTemplate.opsForHash().delete(id, key);
}
@Override
public void clear() {
redisTemplate.delete(id);
}
@Override
public int getSize() {
return redisTemplate.opsForHash().size(id).intValue();
}
}
在自己完成的这个缓存机制类的有参构造通过ApplicationContextUtil去实例化Redis,需要注意的是,这里不能通过Autowire的方式引用redisTemplate,因为RedisCache并不是Spring容器里的bean。所以我们需要手动地去调用容器的getBean方法来拿到这个bean.所以说需要去写一个工具类去实现ApplicationContextAware,从而去获取上下文对象ApplicationContext,然后才能通过getBean去实例化Redis。
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ApplicationContextUtil implements ApplicationContextAware {
//获取IoC容器
private static ApplicationContext applicationContext;
//实现了ApplicationContextAware接口后,重写setApplicationContext方法进行设置
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
//通过在工厂中获取对象的方法
public static <T> T getBean(String beanName,Class<T> requiredType){
return applicationContext.getBean(beanName,requiredType);
}
}
第四步:将我们原先在MyBatis的二级缓存<cache/>修改成<cache type="xxx"/>
测试后就会发现控制台输出的和一开始的二级缓存一样,并且在Redis数据库中也存放了数据