Redis
与传统的关系型数据库不同,其数据存储介质是内存而非硬盘,众所周知内存的读取速度远大于硬盘,因此对于 Redis
在应用中通常扮演者缓存的角色。
其中较为常见的应用场景即将系统的热点数据提前加载至 Redis
中,当应用程序需要访问时直接读取缓存即可,从而提供程序效率。在之前的文章中也介绍了如何在 Spring Boot
工程中通过注解方式实现数据缓存。
这篇文章将介绍如何自定义进行 Redis
数据库基本读写操作,这里的工程我基于之前的项目进行改造,没有看过之前文章的小伙伴可以先简单过一遍:Redis缓存配置。
1. 依赖引入
首先需要在项目中引入 Redis
相关 Maven
依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
2. Bean注入
我们直接在之前的配置类 RedisConfig
中新增如下代码:
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(factory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key 采用 String 的序列化方式
template.setKeySerializer(stringRedisSerializer);
// key 的哈希值采用 String 的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value 序列化方式采用 jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// value 的哈希值序列化方式采用 jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
3. 基本操作
为了后续的读存更加方便,在这里对 RedisTemplate
的接口二次封装为我们自己的工具类。
新建 RedisUtils
类,内容如下:
@Service
public class RedisUtils {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 判断 key 是否存在
*
* @param key 键
*/
public Boolean hasKey(String key) {
if (key == null) {
throw new IllegalArgumentException("key can not be null!");
}
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 获取 key 过期时间
*
* @param key 键
* @return 时间, 0代表为永久有效
*/
public Long getExpire(String key, TimeUnit timeUnit) {
if (key == null) {
throw new IllegalArgumentException("key can not be null!");
}
Long expireTime = -1L;
if (hasKey(key)) {
expireTime = redisTemplate.getExpire(key, timeUnit);
}
return expireTime;
}
/**
* 设置 key 过期时间
*
* @param key 键
* @return 时间, 0代表为永久有效
*/
public Boolean setExpire(String key, long time, TimeUnit timeUnit) {
Boolean flag;
try {
if (hasKey(key)) {
flag = redisTemplate.expire(key, time, timeUnit);
} else {
throw new RuntimeException("The key doesn't exist.");
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return flag;
}
/**
* 缓存放入, 默认无限期
*
* @param key 键
* @param value 值
*/
public void set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 缓存放入并设置过期时间
*
* @param key 键
* @param value 值
* @param time 时间, 0 将设置无限期
*/
public void setWithTime(String key, Object value, long time, TimeUnit timeUnit) {
try {
if (time <= 0) {
set(key, value);
} else {
redisTemplate.opsForValue().set(key, value, time, timeUnit);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 缓存获取
*
* @param key 键
* @return 值
*/
public Object get(String key) {
if (key == null) {
throw new IllegalArgumentException("key can not be null!");
}
Object object = null;
if (hasKey(key)) {
object = redisTemplate.opsForValue().get(key);
}
return object;
}
/**
* 删除数据
*
* @param key 键
*/
public Boolean delete(String key) {
if (key == null) {
throw new IllegalArgumentException("key can not be null!");
}
Boolean flag = false;
if (hasKey(key)) {
flag = redisTemplate.delete(key);
}
return flag;
}
/**
* 批量删除缓存
*
* @param keys 可以传一个值 或多个
*/
public void batchDelete(Collection keys) {
if (!CollectionUtils.isEmpty(keys)) {
redisTemplate.delete(keys);
} else {
throw new IllegalArgumentException("keys can not be null!");
}
}
}
4. 读取示例
这里以之前的 Spring
数据缓存为例,在不通过注解的方式实现数据的缓存存储。
下面我们就可以通过上面封装的方法对 Redis
数据库进行操作了,首先确定一下缓存的业务逻辑:
当业务在读取数据之前,应该先根据指定
Key
在Redis
中进行读取,
- 存在,直接返回缓存数据。
- 不存在,从数据库中查询数据返回,并将结果存入
Redis
缓存中。
其实只是我们将之前注解帮我们做的事自己用代码实现了而已。
通过封装的 RedisService
调用之前封装好的 get()
与 set()
方法,具体示例如下:
public class RedisController {
@Autowired
private UserService userService;
@Autowired
private RedisService redisService;
@GetMapping("/list")
public List<User> get(@Param("accountID") String accountID){
List<User> userList ;
// 先从 Redis 数据库读取数据
userList = (List<User>) redisService.get("users:list");
// 如果未查询到数据再从 MySQL 中读取
if(userList == null) {
userList = userService.list();
// 将查出来的数据存入 Redis 数据库
redisService.set("users:list", userList, 5);
}
return userList;
}
}
5. 写入示例
通过封装的 RedisService
调用之前封装好的 get()
与 set()
方法,具体示例如下:
public class RedisController {
@Autowired
private UserService userService;
@Autowired
private RedisService redisService;
@PostMapping("/add")
public int update(@RequestBody User user) throws Exception {
User userResult = userService.get(user.getAccountID());
if (userResult == null) {
// 根据 Key 删除缓存
redisService.delete("users:list");
return userService.add(user);
} else {
return -1;
}
}
}