前言:最近有不少粉丝关注本公众号。并且我已经成功开通了流量主同时会赚一点点广告费,我打算每个月把这部分钱拿出来给大家买点书刊,算是给大家一点福利吧。大家想买什么书扫描下方的加他拉你加群。最后,非常感谢大家的关注。
如何不太优雅的使用Redis缓存
我们都知道使用redis
来缓存我们的数据集合,如下图所示。
通常自己去缓存数据,这样的优点就是逻辑清晰,而且redis
的key
和value
会比较规范。但是冗余代码会比较多,需要自己进行判断数据是否过期。
为了简化业务代码,现在用注解的方式集成redis
二级缓存,但是他的key
和value
就会比较不符合规范。他的key
一共包含5个部分,最重要的就是sql
和这个sql
的参数。他的value
就是这个查询的结果集。
准备工作
引入依赖,mybatis
<dependency> <groupId>com.zaxxergroupId> <artifactId>HikariCPartifactId>dependency><dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-jdbcartifactId> <exclusions> <exclusion> <groupId>org.apache.tomcatgroupId> <artifactId>tomcat-jdbcartifactId> exclusion> exclusions>dependency><dependency> <groupId>com.baomidougroupId> <artifactId>mybatis-plus-boot-starterartifactId> <version>3.0.6version>dependency><dependency> <groupId>com.baomidougroupId> <artifactId>mybatis-plus-extensionartifactId> <version>3.0.6version>dependency>
redis
依赖
<dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-data-redisartifactId>dependency><dependency> <groupId>org.apache.commonsgroupId> <artifactId>commons-pool2artifactId>dependency>
配置文件
spring: datasource: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver # 这里使用的是 ip:3336/db_order 的数据库(一个服务一个数据库) url: jdbc:mysql://localhost:3306/sys-common?useUnicode=true&characterEncoding=utf-8&serverTimezone=Hongkong&useSSL=false username: root password: 123456 hikari: minimum-idle: 5 idle-timeout: 600000 maximum-pool-size: 10 auto-commit: true pool-name: MyHikariCP max-lifetime: 1800000 connection-timeout: 30000 connection-test-query: SELECT 1 # redis redis: host: xxx.xxx.xxx.xxx port: 6379 lettuce: pool: max-active: 8 max-idle: 8 max-wait: -1ms min-idle: 0 database: 4
配置redisTemplate
import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.serializer.StringRedisSerializer;/** * ProjectName: hello-redis-cache * Package: com.laoshiren.hello.redis.cache.mybatis.configure * ClassName: RedisConfiguration * Author: laoshiren * Description: * Date: 2020/9/13 15:49 * Version: 1.0 */@Configurationpublic class RedisConfiguration { /** * 设置redisTemplate */ @Bean(name = "redisTemplate") public RedisTemplate redisTemplate(LettuceConnectionFactory redisConnectionFactory) { RedisTemplate redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); // 使用String 序列化// redisTemplate.setDefaultSerializer(new StringRedisSerializer());// redisTemplate.setKeySerializer(new StringRedisSerializer());// redisTemplate.setValueSerializer(new StringRedisSerializer()); redisTemplate.afterPropertiesSet(); return redisTemplate; }}
注意这里我不使用String
的序列化方式去序列化Key
和Value
实现
实现Cache
接口
package com.laoshiren.hello.redis.cache.mybatis.cache;import com.laoshiren.hello.redis.cache.mybatis.configure.ApplicationContextHolder;import lombok.extern.slf4j.Slf4j;import org.apache.ibatis.cache.Cache;import org.springframework.data.redis.core.RedisCallback;import org.springframework.data.redis.core.RedisTemplate;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.ReadWriteLock;import java.util.concurrent.locks.ReentrantReadWriteLock;/** * ProjectName: hello-redis-cache * Package: com.laoshiren.hello.redis.cache.mybatis.cache * ClassName: RedisCache * Author: laoshiren * Description: * Date: 2020/9/13 15:34 * Version: 1.0 */@Slf4jpublic class RedisCache implements Cache { private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); private final String id; // cache instance id private RedisTemplate redisTemplate; private static final long EXPIRE_TIME_IN_MINUTES = 30; // redis过期时间 public RedisCache(String id) { if (id == null) { throw new IllegalArgumentException("Cache instances require an ID"); } this.id = id; } @Override public String getId() { return id; } /** * Put query result to redis * * @param key * @param value */ @Override public void putObject(Object key, Object value) { try { RedisTemplate redisTemplate = getRedisTemplate(); redisTemplate.opsForValue().set(key, value, EXPIRE_TIME_IN_MINUTES, TimeUnit.MINUTES); log.debug("Put query result to redis"); } catch (Throwable t) { log.error("Redis put failed", t); } } /** * Get cached query result from redis * * @param key * @return */ @Override public Object getObject(Object key) { try { RedisTemplate redisTemplate = getRedisTemplate(); log.info("Get cached query result from redis");// System.out.println("****" + opsForValue.get(key).toString()); return redisTemplate.opsForValue().get(key); } catch (Throwable t) { log.error("Redis get failed, fail over to db", t); return null; } } /** * Remove cached query result from redis * * @param key * @return */ @Override @SuppressWarnings("unchecked") public Object removeObject(Object key) { try { RedisTemplate redisTemplate = getRedisTemplate(); redisTemplate.delete( key.toString()); log.debug("Remove cached query result from redis"); } catch (Throwable t) { log.error("Redis remove failed", t); } return null; } /** * Clears this cache instance */ @Override public void clear() { RedisTemplate redisTemplate = getRedisTemplate(); redisTemplate.execute((RedisCallback) connection -> { connection.flushDb(); return null; }); log.debug("Clear all the cached query result from redis"); } /** * This method is not used * * @return */ @Override public int getSize() { return 0; } @Override public ReadWriteLock getReadWriteLock() { return readWriteLock; } private RedisTemplate getRedisTemplate() { if (redisTemplate == null) { redisTemplate = ApplicationContextHolder.getBean("redisTemplate"); } return redisTemplate; }}
给指定的mapper
配置缓存
@CacheNamespace(implementation = RedisCache.class)public interface TbPostMapper extends BaseMapper<TbPost> {}
测试
请求一次数据库,使用Debug
模式,它的key
是一个CacheKey
,无法使用使用StringRedisSerializer
去序列化,所以redisTemplate
得使用默认的序列化,即 JdkSerializationRedisSerializer
打开RDM
,看一下4号库。
发现key
和value
就不是很美观,不过不影响使用,当然您可以使用StringRedisSerializer
去实现,只不过我在尝试的过程中获取sql
和参数的时候,会出现一点问题。希望有大佬可以指出。
带参数的sql
特别注意! 在分页缓存的时候,Page
对象的total
必须自己手动查询一次,不然返回给前端的对象里第一次还有总页数,第二次由于走了缓存就不带这个total
,所以必须手动查询一次。
@GetMapping("page/{pageNo}/{pageSize}")public ResponseResult> page(@PathVariable(name = "pageNo")Integer pageNo, @PathVariable(name = "pageSize") Integer pageSize, HttpServletRequest request){ IPage wherePage = new Page<>(pageNo, pageSize); String word = request.getParameter("wd"); LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); if (StringUtils.isNotBlank(word)) { queryWrapper.like(Area::getAreaName,word); } int count = areaService.count(queryWrapper); IPage page = areaService.page(wherePage,queryWrapper); page.setTotal((long)count); return new ResponseResult<>(CodeStatus.OK,"操作成功",page);}
好了,最后还是借用大佬的一句话:“不经一番寒彻骨,怎知梅花扑鼻香”。
打油诗 我不在乎我的作品文章是被现在的人读还是由子孙后代来读。既然上帝花了六千年来等一位观察者,我可以花上一个世纪来等待读者。永久激活方案~
2020-07-29
spring 状态机
2020-05-12
mybatis用到的设计模式
2020-07-02
jvm高级面试题(必须看)
2020-07-23
MySQL索引实现原理分析
2020-05-19
Spring中的用到的设计模式
2020-04-23
Spring 和 SpringBoot 之间到底有啥区别?
2020-05-29
如何快速搭建一个免费的 鉴黄 平台
2020-08-15
美国也就那么回事吧
2020-08-15
5T的Java视频教程全部免费获取
2020-08-14