失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > springboot配置mybatis redis缓存

springboot配置mybatis redis缓存

时间:2020-04-01 15:19:53

相关推荐

springboot配置mybatis redis缓存

一、概述

首先来了解下mybatis 缓存,mybatis缓存分为一级缓存和二级缓存。一级缓存是默认开启的,无需其他配置操作,二级缓存则需要手动设置开启。

一级缓存原理:

Mybatis的一级缓存是指同一个SqlSession中的操作。一级缓存的作用域是一个SqlSession。

在同一个SqlSession中,执行相同的查询SQL,第一次会去查询数据库,并写到缓存中;第二次直接从缓存中取。当执行SQL时两次查询中间发生了增删改操作,则SqlSession的缓存清空。

二级缓存原理:

Mybatis的二级缓存是指mapper映射文件。二级缓存是多个sqlSession共享的,其作用域是mapper下的同一个namespace。

在不同的sqlSession中,相同的namespace下,相同的查询sql语句并且参数也相同的情况下,会命中二级缓存。如果调用相同namespace下的mapper映射文件中的增删改SQL,并执行了commit操作。此时会清空该namespace下的二级缓存。

二、代码实现

了解一些基本原理后,我们开始在springboot集成mybatis的情况下,开启二级缓存。

在pom.xml文件中引入mybatis和redis的依赖

<!--mybatis 依赖包--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>1.3.2</version></dependency><!--mysql--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!--redis lettuce--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>mons</groupId><artifactId>commons-pool2</artifactId></dependency>

在application.yml文件中配置mybatis相关设置时,开启二级缓存

### mybatis相关配置mybatis:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl#开启MyBatis的二级缓存cache-enabled: truemapper-locations: classpath*:mappers/*Mapper.xml### Redis 相关配置redis:host: localhostport: 6379timeout: 10000database: 0lettuce:pool:max-active: 8max-wait: -1max-idle: 8min-idle: 0

实体类实现序列化

我们采用的redis序列化方式是默认的jdk序列化。所以数据库的查询对象(比如Student类)需要实现Serializable接口。

public class Student implements Serializable {//采用的redis序列化方式是默认的jdk序列化。所以数据库的查询对象实体需要实现Serializable接口。private static final long serialVersionUID = 1L;private int id;private String name;private int age;private String position;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getPosition() {return position;}public void setPosition(String position) {this.position = position;}@Overridepublic String toString() {return "Student{" +"id=" + id +", name='" + name + '\'' +", age=" + age +", position='" + position + '\'' +'}';}}

4、先看一下Redis的配置类(这里用的是lettuce)

@Configurationpublic class RedisConfig {@Autowiredprivate LettuceConnectionFactory connectionFactory;@Beanpublic RedisTemplate<String,Object> redisTemplate() {RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();initDomainRedisTemplate(redisTemplate, connectionFactory);return redisTemplate;}/*** 设置数据存入 redis 的序列化方式* @param template* @param factory*/private void initDomainRedisTemplate(RedisTemplate<String, Object> template,LettuceConnectionFactory factory) {// 定义 key 的序列化方式为 string// 需要注意这里Key使用了 StringRedisSerializer,那么Key只能是String类型的,不能为Long,Integer,否则会报错抛异常。StringRedisSerializer redisSerializer = new StringRedisSerializer();template.setKeySerializer(redisSerializer);// 定义 value 的序列化方式为 json@SuppressWarnings({"rawtypes", "unchecked"})Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);template.setValueSerializer(jackson2JsonRedisSerializer);//hash结构的key和value序列化方式template.setHashKeySerializer(jackson2JsonRedisSerializer);template.setHashValueSerializer(jackson2JsonRedisSerializer);template.setEnableTransactionSupport(true);template.setConnectionFactory(factory);}}

5、缓存配置类

public class MybatisRedisCache implements Cache {private static final Logger log = LoggerFactory.getLogger(MybatisRedisCache.class);private String id;private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();//private static final long EXPIRE_TIME_IN_MINUTES = 30; // redis过期时间public MybatisRedisCache(String id) {this.id = id;}private RedisTemplate<Object, Object> getRedisTemplate(){return ApplicationContextHolder.getBean("redisTemplate");}@Overridepublic String getId() {return id;}@Overridepublic void putObject(Object key, Object value) {RedisTemplate redisTemplate = getRedisTemplate();redisTemplate.boundHashOps(getId()).put(key, value);log.info("[结果放入到缓存中: " + key + "=" + value+" ]");}@Overridepublic Object getObject(Object key) {RedisTemplate redisTemplate = getRedisTemplate();Object value = redisTemplate.boundHashOps(getId()).get(key);log.info("[从缓存中获取了: " + key + "=" + value+" ]");return value;}@Overridepublic Object removeObject(Object key) {RedisTemplate redisTemplate = getRedisTemplate();Object value = redisTemplate.boundHashOps(getId()).delete(key);log.info("[从缓存删除了: " + key + "=" + value+" ]");return value;}@Overridepublic void clear() {RedisTemplate redisTemplate = getRedisTemplate();redisTemplate.delete(getId());log.info("清空缓存!!!");}@Overridepublic int getSize() {RedisTemplate redisTemplate = getRedisTemplate();Long size = redisTemplate.boundHashOps(getId()).size();return size == null ? 0 : size.intValue();}@Overridepublic ReadWriteLock getReadWriteLock() {return readWriteLock;}}

ps:

重点部分就是重写这个mybatis的cache类,它只会对配置文件类型的映射文件起作用。

该接口共有以下五个方法:

String getId():mybatis缓存操作对象的标识符。一个mapper对应一个mybatis的缓存操作对象。

void putObject(Object key, Object value):将查询结果塞入缓存。

Object getObject(Object key):从缓存中获取被缓存的查询结果。

Object removeObject(Object key):从缓存中删除对应的key、value。只有在回滚时触发。

void clear():发生更新时,清除缓存。

int getSize():可选实现。返回缓存的数量。

ReadWriteLock getReadWriteLock():可选实现。用于实现原子性的缓存操作。

上述重写cache类中有几个关键点:

自定义实现的二级缓存,必须要有一个带id的构造函数,否则会报错。此处使用Spring封装的redisTemplate来操作Redis。很多都是直接用jedis库,但是现在springboot2.x 以上对lettuce的兼容更好。RedisTemplate封装了底层的实现,使用redisTemplate会更加方便,无论是使用jedis还是使用lettuce,我们可以直接更换底层的库,无需修改上层代码。这里不能通过@Autowire的方式引用redisTemplate,因为RedisCache并不是Spring容器里的bean。所以我们需要手动地去调用容器的getBean方法来拿到这个bean,那么这样,我们就需要引入ApplicationContextHolder这个类。 ApplicationContextHolder.java (我们需要通过这个类得到RedisTemplate)

@Componentpublic class ApplicationContextHolder implements ApplicationContextAware{private static ApplicationContext applicationContext;/*** 实现ApplicationContextAware接口的context注入函数, 将其存入静态变量.*/public void setApplicationContext(ApplicationContext applicationContext) {ApplicationContextHolder.applicationContext = applicationContext; // NOSONAR}/*** 取得存储在静态变量中的ApplicationContext.*/public static ApplicationContext getApplicationContext() {checkApplicationContext();return applicationContext;}/*** 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型.*/@SuppressWarnings("unchecked")public static <T> T getBean(String name) {checkApplicationContext();return (T) applicationContext.getBean(name);}/*** 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型.*/@SuppressWarnings("unchecked")public static <T> T getBean(Class<T> clazz) {checkApplicationContext();return (T) applicationContext.getBeansOfType(clazz);}/*** 清除applicationContext静态变量.*/public static void cleanApplicationContext() {applicationContext = null;}private static void checkApplicationContext() {if (applicationContext == null) {throw new IllegalStateException("applicaitonContext未注入,请在applicationContext.xml中定义SpringContextHolder");}}}

7、然后再映射文件中开启二级缓存(使用二级缓存)

<mapper namespace="com.example.demo.dao.StudentDao"><!-- 开启基于redis的二级缓存 --><cache type="com.example.demo.redis.cache.MybatisRedisCache"/><cache/><insert id="insert" parameterType="com.example.demo.entity.Student" useGeneratedKeys="true" keyProperty="id">insert intostudents(name,age,position)values(#{name},#{age},#{position})</insert><insert id="batchInsert" parameterType="java.util.List" useGeneratedKeys="true" keyProperty="id">insert intostudents(name,age,position)values<foreach collection="studentList" item="item" index="index" open="" close="" separator=",">(#{item.name},#{item.age},#{item.position})</foreach></insert><delete id="delete" parameterType="java.lang.String">delete from students where name = #{name}</delete><!--并且在update语句中,设置flushCache为true,这样在更新信息时,能够自动失效缓存(本质上调用的是clear方法)--><update id="update" parameterType="com.example.demo.entity.Student" flushCache="true">update studentsset students.position = #{position}where name = #{name}</update><select id="findByName" resultMap="BaseResultMap">select *from studentswhere name = #{name}</select><select id="findAll" resultMap="BaseResultMap">select *from students</select><resultMap id="BaseResultMap" type="com.example.demo.entity.Student"><result column="name" property="name"/><result column="age" property="age"/><result column="position" property="position"/></resultMap></mapper>

下面是我在实现二级缓存过程中一些报错问题:

在我修改了序列化问题后,报错消失。

如果觉得《springboot配置mybatis redis缓存》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。