springboot之使用redistemplate优雅地操作redis


概述本文内容主要

关于spring-redis

关于redis的key设计

redis的基本数据结构

介绍redis与springboot的整合

sringboot中的redistemplate的使用

之前看了很多博客,大都都只是粗略的介绍,这里想要记录的全面一些,也算是一个学习的过程

首发于我的个人博客:janti的个人博客

关于spring-redis

spring-data-redis针对jedis提供了如下功能:

1. 连接池自动管理,提供了一个高度封装的“RedisTemplate”类

2. 针对jedis客户端中大量api进行了归类封装,将同一类型操作封装为operation接口

ValueOperations:简单K-V操作

SetOperations:set类型数据操作

ZSetOperations:zset类型数据操作

HashOperations:针对map类型的数据操作

ListOperations:针对list类型的数据操作

3. 提供了对key的“bound”(绑定)便捷化操作API,可以通过bound封装指定的key,然后进行一系列的操作而无须“显式”的再次指定Key,即BoundKeyOperations:

BoundValueOperations

BoundSetOperations

BoundListOperations

BoundSetOperations

BoundHashOperations

4. 将事务操作封装,有容器控制。

5. 针对数据的“序列化/反序列化”,提供了多种可选择策略(RedisSerializer)

JdkSerializationRedisSerializer:POJO对象的存取场景,使用JDK本身序列化机制,将pojo类通过ObjectInputStream/ObjectOutputStream进行序列化操作,最终redis-server中将存储字节序列。是目前最常用的序列化策略。

StringRedisSerializer:Key或者value为字符串的场景,根据指定的charset对数据的字节序列编码成string,是“new String(bytes, charset)”和“string.getBytes(charset)”的直接封装。是最轻量级和高效的策略。

JacksonJsonRedisSerializer:jackson-json工具提供了javabean与json之间的转换能力,可以将pojo实例序列化成json格式存储在redis中,也可以将json格式的数据转换成pojo实例。因为jackson工具在序列化和反序列化时,需要明确指定Class类型,因此此策略封装起来稍微复杂。【需要jackson-mapper-asl工具支持】

OxmSerializer:提供了将javabean与xml之间的转换能力,目前可用的三方支持包括jaxb,apache-xmlbeans;redis存储的数据将是xml工具。不过使用此策略,编程将会有些难度,而且效率最低;不建议使用。【需要spring-oxm模块的支持】

如果你的数据需要被第三方工具解析,那么数据应该使用StringRedisSerializer而不是JdkSerializationRedisSerializer。

关于key的设计

key的存活时间:

无论什么时候,只要有可能就利用key超时的优势。一个很好的例子就是储存一些诸如临时认证key之类的东西。当你去查找一个授权key时——以OAUTH为例——通常会得到一个超时时间。

这样在设置key的时候,设成同样的超时时间,Redis就会自动为你清除。

关系型数据库的redis

1: 把表名转换为key前缀 如, tag:

2: 第2段放置用于区分区key的字段--对应mysql中的主键的列名,如userid

3: 第3段放置主键值,如2,3,4...., a , b ,c

4: 第4段,写要存储的列名

例:user:userid:9:username

Redis的数据类型

String字符串

string是redis最基本的类型,一个key对应一个value。

string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。

string类型是Redis最基本的数据类型,一个键最大能存储512MB。

String类型的操作参考

链表

redis列表是简单的字符串列表,排序为插入的顺序。列表的最大长度为2^32-1。

redis的列表是使用链表实现的,这意味着,即使列表中有上百万个元素,增加一个元素到列表的头部或尾部的操作都是在常量的时间完成。

可以用列表获取最新的内容(像帖子,微博等),用ltrim很容易就会获取最新的内容,并移除旧的内容。

用列表可以实现生产者消费者模式,生产者调用lpush添加项到列表中,消费者调用rpop从列表中提取,如果没有元素,则轮询去获取,或者使用brpop等待生产者添加项到列表中。

List类型的操作参考

集合

redis集合是无序的字符串集合,集合中的值是唯一的,无序的。可以对集合执行很多操作,例如,测试元素是否存在,对多个集合执行交集、并集和差集等等。

我们通常可以用集合存储一些无关顺序的,表达对象间关系的数据,例如用户的角色,可以用sismember很容易就判断用户是否拥有某个角色。

在一些用到随机值的场合是非常适合的,可以用 srandmember/spop 获取/弹出一个随机元素。

同时,使用@EnableCaching开启声明式缓存支持,这样就可以使用基于注解的缓存技术。注解缓存是一个对缓存使用的抽象,通过在代码中添加下面的一些注解,达到缓存的效果。

Set类型的操作参考

ZSet 有序集合

有序集合由唯一的,不重复的字符串元素组成。有序集合中的每个元素都关联了一个浮点值,称为分数。可以把有序看成hash和集合的混合体,分数即为hash的key。

有序集合中的元素是按序存储的,不是请求时才排序的。

ZSet类型的操作类型

Hash-哈希

redis的哈希值是字符串字段和字符串之间的映射,是表示对象的完美数据类型。

哈希中的字段数量没有限制,所以可以在你的应用程序以不同的方式来使用哈希。

Hash类型的操作参考

springboot 与redis的整合

pom文件

依赖如下:

org.springframework.boot

spring-boot-starter-parent

1.5.1.RELEASE

org.springframework.boot

spring-boot-starter-web

org.springframework.boot

spring-boot-starter-thymeleaf

org.springframework.boot

spring-boot-starter-test

test

org.springframework.boot

spring-boot-starter-data-redis

application.properties

# Redis数据库索引(默认为0)

spring.redis.database=0

# Redis服务器地址

spring.redis.host=127.0.0.1

# Redis服务器连接端口

spring.redis.port=6379

# Redis服务器连接密码(默认为空)

spring.redis.password=

# 连接池最大连接数(使用负值表示没有限制)

spring.redis.pool.max-active=8

# 连接池最大阻塞等待时间(使用负值表示没有限制)

spring.redis.pool.max-wait=-1

# 连接池中的最大空闲连接

spring.redis.pool.max-idle=8

# 连接池中的最小空闲连接

spring.redis.pool.min-idle=0

# 连接超时时间(毫秒)

spring.redis.timeout=0

redisTemplate的配置

新建一个redisConfig类,进行相关bean的配置:

package com.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;

import com.fasterxml.jackson.annotation.PropertyAccessor;

import com.fasterxml.jackson.databind.ObjectMapper;

import org.springframework.cache.CacheManager;

import org.springframework.cache.annotation.CachingConfigurerSupport;

import org.springframework.cache.annotation.EnableCaching;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.data.redis.cache.RedisCacheManager;

import org.springframework.data.redis.connection.RedisConnectionFactory;

import org.springframework.data.redis.core.*;

import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;

import org.springframework.data.redis.serializer.StringRedisSerializer;

/**

* @author janti

* reids 相关bean的配置

*/

@Configuration

@EnableCaching

public class RedisConfig extends CachingConfigurerSupport {

/**

* 选择redis作为默认缓存工具

* @param redisTemplate

* @return

*/

@Bean

public CacheManager cacheManager(RedisTemplate redisTemplate) {

RedisCacheManager rcm = new RedisCacheManager(redisTemplate);

return rcm;

}

/**

* retemplate相关配置

* @param factory

* @return

*/

@Bean

public RedisTemplate redisTemplate(RedisConnectionFactory factory) {

RedisTemplate template = new RedisTemplate<>();

// 配置连接工厂

template.setConnectionFactory(factory);

//使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)

Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);

ObjectMapper om = new ObjectMapper();

// 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public

om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);

// 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常

om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);

jacksonSeial.setObjectMapper(om);

// 值采用json序列化

template.setValueSerializer(jacksonSeial);

//使用StringRedisSerializer来序列化和反序列化redis的key值

template.setKeySerializer(new StringRedisSerializer());

// 设置hash key 和value序列化模式

template.setHashKeySerializer(new StringRedisSerializer());

template.setHashValueSerializer(jacksonSeial);

template.afterPropertiesSet();

return template;

}

/**

* 对hash类型的数据操作

*

* @param redisTemplate

* @return

*/

@Bean

public HashOperations hashOperations(RedisTemplate redisTemplate) {

return redisTemplate.opsForHash();

}

/**

* 对redis字符串类型数据操作

*

* @param redisTemplate

* @return

*/

@Bean

public ValueOperations valueOperations(RedisTemplate redisTemplate) {

return redisTemplate.opsForValue();

}

/**

* 对链表类型的数据操作

*

* @param redisTemplate

* @return

*/

@Bean

public ListOperations listOperations(RedisTemplate redisTemplate) {

return redisTemplate.opsForList();

}

/**

* 对无序集合类型的数据操作

*

* @param redisTemplate

* @return

*/

@Bean

public SetOperations setOperations(RedisTemplate redisTemplate) {

return redisTemplate.opsForSet();

}

/**

* 对有序集合类型的数据操作

*

* @param redisTemplate

* @return

*/

@Bean

public ZSetOperations zSetOperations(RedisTemplate redisTemplate) {

return redisTemplate.opsForZSet();

}

}

spring-redis中使用了RedisTemplate来进行redis的操作,通过泛型的K和V设置键值对的对象类型。这里使用了string作为key的对象类型,值为Object。

对于Object,spring-redis默认使用了jdk自带的序列化,不推荐使用默认了。所以使用了json的序列化方式

对spring-redis对redis的五种数据类型也有支持

HashOperations:对hash类型的数据操作

ValueOperations:对redis字符串类型数据操作

ListOperations:对链表类型的数据操作

SetOperations:对无序集合类型的数据操作

ZSetOperations:对有序集合类型的数据操作

redis操作的工具类

package com.service;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.data.redis.core.RedisTemplate;

import org.springframework.stereotype.Component;

import java.util.Collection;

import java.util.Date;

import java.util.Set;

import java.util.concurrent.TimeUnit;

import java.util.stream.Collectors;

import java.util.stream.Stream;

@Component

public class RedisService {

@Autowired

private RedisTemplate redisTemplate;

/**

* 默认过期时长,单位:秒

*/

public static final long DEFAULT_EXPIRE = 60 * 60 * 24;

/**

* 不设置过期时长

*/

public static final long NOT_EXPIRE = -1;

public boolean existsKey(String key) {

return redisTemplate.hasKey(key);

}

/**

* 重名名key,如果newKey已经存在,则newKey的原值被覆盖

*

* @param oldKey

* @param newKey

*/

public void renameKey(String oldKey, String newKey) {

redisTemplate.rename(oldKey, newKey);

}

/**

* newKey不存在时才重命名

*

* @param oldKey

* @param newKey

* @return 修改成功返回true

*/

public boolean renameKeyNotExist(String oldKey, String newKey) {

return redisTemplate.renameIfAbsent(oldKey, newKey);

}

/**

* 删除key

*

* @param key

*/

public void deleteKey(String key) {

redisTemplate.delete(key);

}

/**

* 删除多个key

*

* @param keys

*/

public void deleteKey(String... keys) {

Set kSet = Stream.of(keys).map(k -> k).collect(Collectors.toSet());

redisTemplate.delete(kSet);

}

/**

* 删除Key的集合

*

* @param keys

*/

public void deleteKey(Collection keys) {

Set kSet = keys.stream().map(k -> k).collect(Collectors.toSet());

redisTemplate.delete(kSet);

}

/**

* 设置key的生命周期

*

* @param key

* @param time

* @param timeUnit

*/

public void expireKey(String key, long time, TimeUnit timeUnit) {

redisTemplate.expire(key, time, timeUnit);

}

/**

* 指定key在指定的日期过期

*

* @param key

* @param date

*/

public void expireKeyAt(String key, Date date) {

redisTemplate.expireAt(key, date);

}

/**

* 查询key的生命周期

*

* @param key

* @param timeUnit

* @return

*/

public long getKeyExpire(String key, TimeUnit timeUnit) {

return redisTemplate.getExpire(key, timeUnit);

}

/**

* 将key设置为永久有效

*

* @param key

*/

public void persistKey(String key) {

redisTemplate.persist(key);

}

}

redis的key工具类

package com.util;

/**

* redisKey设计

*/

public class RedisKeyUtil {

/**

* redis的key

* 形式为:

* 表名:主键名:主键值:列名

*

* @param tableName 表名

* @param majorKey 主键名

* @param majorKeyValue 主键值

* @param column 列名

* @return

*/

public static String getKeyWithColumn(String tableName,String majorKey,String majorKeyValue,String column){

StringBuffer buffer = new StringBuffer();

buffer.append(tableName).append(":");

buffer.append(majorKey).append(":");

buffer.append(majorKeyValue).append(":");

buffer.append(column);

return buffer.toString();

}

/**

* redis的key

* 形式为:

* 表名:主键名:主键值

*

* @param tableName 表名

* @param majorKey 主键名

* @param majorKeyValue 主键值

* @return

*/

public static String getKey(String tableName,String majorKey,String majorKeyValue){

StringBuffer buffer = new StringBuffer();

buffer.append(tableName).append(":");

buffer.append(majorKey).append(":");

buffer.append(majorKeyValue).append(":");

return buffer.toString();

}

}

如何使用?

测试代码

新建一个实体类:

package com.domain;

public class UserVo {

public static final String Table = "t_user";

private String name;

private String address;

private Integer age;

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public String getAddress() {

return address;

}

public void setAddress(String address) {

this.address = address;

}

public Integer getAge() {

return age;

}

public void setAge(Integer age) {

this.age = age;

}

@Override

public String toString() {

return "UserVo{" +

"name='" + name + ''' +

", address='" + address + ''' +

", age=" + age +

'}';

}

}

再新建一个测试类:

package com.config;

import com.domain.UserVo;

import com.service.RedisService;

import com.util.RedisKeyUtil;

import org.junit.Test;

import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.test.context.SpringBootTest;

import org.springframework.data.redis.core.*;

import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;

import java.util.Set;

import java.util.concurrent.TimeUnit;

import static org.junit.Assert.*;

@RunWith(SpringRunner.class)

@SpringBootTest

public class RedisConfigTest {

@Autowired

private StringRedisTemplate stringRedisTemplate;

@Autowired

private RedisTemplate redisTemplate;

@Resource

private ValueOperations valueOperations;

@Autowired

private HashOperations hashOperations;

@Autowired

private ListOperations listOperations;

@Autowired

private SetOperations setOperations;

@Autowired

private ZSetOperations zSetOperations;

@Resource

private RedisService redisService;

@Test

public void testObj() throws Exception{

UserVo userVo = new UserVo();

userVo.setAddress("上海");

userVo.setName("测试dfas");

userVo.setAge(123);

ValueOperations operations = redisTemplate.opsForValue();

redisService.expireKey("name",20, TimeUnit.SECONDS);

String key = RedisKeyUtil.getKey(UserVo.Table,"name",userVo.getName());

UserVo vo = (UserVo) operations.get(key);

System.out.println(vo);

}

@Test

public void testValueOption( )throws Exception{

UserVo userVo = new UserVo();

userVo.setAddress("上海");

userVo.setName("jantent");

userVo.setAge(23);

valueOperations.set("test",userVo);

System.out.println(valueOperations.get("test"));

}

@Test

public void testSetOperation() throws Exception{

UserVo userVo = new UserVo();

userVo.setAddress("北京");

userVo.setName("jantent");

userVo.setAge(23);

UserVo auserVo = new UserVo();

auserVo.setAddress("n柜昂周");

auserVo.setName("antent");

auserVo.setAge(23);

setOperations.add("user:test",userVo,auserVo);

Set result = setOperations.members("user:test");

System.out.println(result);

}

@Test

public void HashOperations() throws Exception{

UserVo userVo = new UserVo();

userVo.setAddress("北京");

userVo.setName("jantent");

userVo.setAge(23);

hashOperations.put("hash:user",userVo.hashCode()+"",userVo);

System.out.println(hashOperations.get("hash:user",userVo.hashCode()+""));

}

@Test

public void ListOperations() throws Exception{

UserVo userVo = new UserVo();

userVo.setAddress("北京");

userVo.setName("jantent");

userVo.setAge(23);

// listOperations.leftPush("list:user",userVo);

// System.out.println(listOperations.leftPop("list:user"));

// pop之后 值会消失

System.out.println(listOperations.leftPop("list:user"));

}

}


注解缓存的使用

@Cacheable:在方法执行前Spring先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;没有则调用方法并将方法返回值放进缓存。

@CachePut:将方法的返回值放到缓存中。

@CacheEvict:删除缓存中的数据。

页面更新:2024-05-24

标签:操作   生产者   注解   字段   字符串   缓存   数据类型   优雅   元素   对象   策略   类型   时间   工具   数据   列表   科技

1 2 3 4 5

上滑加载更多 ↓
Top