【Spring学习】Spring Data Redis:RedisTemplate、Repository、Cache注解

1,spring-data-redis官网

1)特点

  1. 提供了对不同Redis客户端的整合(Lettuce和Jedis)
  2. 提供了RedisTemplate统一API来操作Redis
  3. 支持Redis的发布订阅模型
  4. 支持Redis哨兵和Redis集群
  5. 支持基于Lettuce的响应式编程
  6. 支持基于JDK、JSON、字符串、Spring独享的数据序列化及反序列化
  7. 支持基于Redis的JDKCollection实现

2,RedisTemplate

SpringDataRedis中提供了RedisTemplate工具类,其中封装了各种对Redis的操作

1)常用API

api 说明
redisTemplate.opsForValue(); 操作字符串
redisTemplate.opsForHash(); 操作hash
redisTemplate.opsForList(); 操作list
redisTemplate.opsForSet(); 操作set
redisTemplate.opsForZSet(); 操作有序set
redisTemplate.expire(key, 60 * 10000 * 30, TimeUnit.MILLISECONDS); 设置过期时间

1>API:String

java 复制代码
redisTemplate.opsForValue().set("name","tom")
说明 api 备注
添加单值 .set(key,value)
获取单值 .get(key)
添加单值并返回这个值是否已经存在 .setIfAbsent(key, value)
添加单值并返回旧值 .getAndSet(key, value)
批量添加 Map<String,String> maps; .multiSet(maps)
批量获取 List<String> keys; .multiGet(keys)
数值型+1 .increment(key,1)
设置过期时间 set(key, value, timeout, TimeUnit.SECONDS) 过期返回null
字符串追加 .append(key,"Hello");

2>API:List数据

java 复制代码
template.opsForList().range("list",0,-1)
说明 api 备注
单个插入 Long leftPush(key, value); 返回操作后的列表的长度
批量插入 Long leftPushAll(K key, V... values); 返回操作后的列表的长度; values可以是String[]List<Object>
查看 .range(key,0,-1) 从左往右:0,1,2; 从右往左:-1,-2,-3; 可做分页
弹出最左边的元素 .leftPop("list") 弹出之后该值在列表中将不复存在
修改 set(key, index, value)
key存在则插入 Long rightPushIfPresent(K key, V value); 返回操作后的列表的长度
求subList .trim(key,1,-1)
移除元素 Long remove(key, long count, Object value); count> 0:删除从左到右共count个等于value的元素。 count <0:删除等于从右到左共count个等于value的元素。 count = 0:删除等于value的所有元素。
求长度 .size(key)

3>API:Hash操作

一个key1对应一个Map,map中每个value中@class后面对应的值为类信息。

java 复制代码
template.opsForHash().put("redisHash","name","tom");
说明 api 备注
单插入 .put(redisKey,hashKey, value)
批量插入 Map<String,Object> map .putAll(key, map)
查单数据 .get(redisKey,hashKey)
查所有数据 .entries(redisHash)
查key是否存在 Boolean hasKey(redisKey, Object hashKey);
批量获取Hash值 List multiGet(redisKey, List<Object> kes);
获取key集合 Set keys(redisKey)
批量删除 Long delete(redisKey, Object... hashKeys)
数值型value + 5 increment(redisKey, hashKey, 5) 返回操作后的value值
hashkey不存在时设置value Boolean putIfAbsent(redisKey,hashKey, value) 存在返回true,不存在返回true

遍历:

java 复制代码
Cursor<Map.Entry<Object, Object>> curosr = template.opsForHash().scan("redisHash", ScanOptions.ScanOptions.NONE);
        while(curosr.hasNext()){
            Map.Entry<Object, Object> entry = curosr.next();
            System.out.println(entry.getKey()+":"+entry.getValue());
        }

4>API:Set数据

java 复制代码
template.opsForSet().add(k,v)
说明 api 备注
添加 Long add(key, V... values); values可以是:String[]
查看所有 .members(key)
查询长度 .size(key)
查询元素是否存在 Boolean isMember(key, Object o);
批量删除 Long remove(key, Object... values); values可以是:String[]
随机移除 V pop(K key);
将元素value 从 sourcekey所在集合移动到 destKey所在集合 Boolean move(sourcekey, V value, destKey) 移动后sourcekey集合再没有value元素,destKey集合去重。
求两个集合的交集 Set intersect(K key, K otherKey);
求多个无序集合的交集 Set intersect(K key, Collection otherKeys);
求多个无序集合的并集 Set union(K key, Collection otherKeys);

遍历:

java 复制代码
Cursor<Object> curosr = template.opsForSet().scan("setTest", ScanOptions.NONE);
        while(curosr.hasNext()){
            System.out.println(curosr.next());
        }

5>API:ZSet集合

有序的Set集合,排序依据是Score。

java 复制代码
template.opsForZSet().add("zset1","zset-1",1.0)
说明 api 备注
添加单个元素 Boolean add(k, v, double score) 返回元素是否已存在
批量添加元素 Long add(k, Set<TypedTuple> tuples) 举例:见下文1.
批量删除 Long remove(K key, Object... values);
排序按分数值asc,返回成员o的排名 Long rank(key, Object o); 排名从0开始
排序按分数值desc,返回成员o的排名 Long reverseRank(key, Object o); 排名从0开始
按区间查询,按分数值asc Set range(key, 0, -1);
增加元素的score值,并返回增加后的值 Double incrementScore(K key, V value, double delta);
  1. 批量添加元素
java 复制代码
ZSetOperations.TypedTuple<Object> objectTypedTuple1 = new DefaultTypedTuple<Object>("zset-5",9.6);
        ZSetOperations.TypedTuple<Object> objectTypedTuple2 = new DefaultTypedTuple<Object>("zset-6",9.9);
        Set<ZSetOperations.TypedTuple<Object>> tuples = new HashSet<ZSetOperations.TypedTuple<Object>>();
        tuples.add(objectTypedTuple1);
        tuples.add(objectTypedTuple2);
        System.out.println(template.opsForZSet().add("zset1",tuples));
        System.out.println(template.opsForZSet().range("zset1",0,-1));
  1. 遍历
java 复制代码
Cursor<ZSetOperations.TypedTuple<Object>> cursor = template.opsForZSet().scan("zzset1", ScanOptions.NONE);
        while (cursor.hasNext()){
            ZSetOperations.TypedTuple<Object> item = cursor.next();
            System.out.println(item.getValue() + ":" + item.getScore());
        }

2)使用

1>依赖

xml 复制代码
<!--        Redis依赖-->
        <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>配置文件

yml 复制代码
spring:
  redis:
    host: 127.0.0.1  # Redis服务器地址
    port: 6379       # Redis服务器连接端口 
    timeout:0        # 连接超时时间(毫秒)
#   database: 0      # Redis数据库索引(默认为0)
#   password:        # Redis服务器连接密码(默认为空)
    lettuce:         # 使用的是lettuce连接池
      pool:
        max-active: 8 # 连接池最大连接数(使用负值表示没有限制)
        max-idle: 8   # 连接池中的最大空闲连接
        min-idle: 0   # 连接池中的最小空闲连接
        max-wait: 100 # 连接池最大阻塞等待时间(使用负值表示没有限制)

1>序列化配置

RedisTemplate默认采用JDK的序列化工具,序列化为字节形式,在redis中可读性很差。

修改默认的序列化方式为jackson:

java 复制代码
@Configuration
public class RedisConfig {
    @Bean     //RedisConnectionFactory不需要我们创建Spring会帮助我们创建
    public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory connectionFactory){
//        1.创建RedisTemplate对象
          RedisTemplate<String,Object> template = new RedisTemplate<>();
//        2.设置连接工厂
          template.setConnectionFactory(connectionFactory);
//        3.创建JSON序列化工具
          GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
//        4.设置Key的序列化
          template.setKeySerializer(RedisSerializer.string());
          template.setHashKeySerializer(RedisSerializer.string());
//        5.设置Value的序列化   jsonRedisSerializer使我们第三步new出来的
          template.setValueSerializer(jsonRedisSerializer);
          template.setHashValueSerializer(jsonRedisSerializer);
//        6.返回
        return template;
        
    }
}

但是json序列号可能导致一些其他的问题:JSON序列化器会将类的class类型写入到JSON结果中并存入Redis,会带来额外的内存开销。

为了节省内存空间,我们并不会使用JSON序列化器来处理value,而是统一使用String序列化器,要求只能存储String类型的key哈value,当要存储Java对象时,手动完成对象的序列化和反序列化。

4>java实现

java 复制代码
public class RedisUtil {
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 批量删除对应的value
     * 
     * @param keys
     */
    public void remove(final String... keys) {
        for (String key : keys) {
            remove(key);
        }
    }

    /**
     * 批量删除key
     * 
     * @param pattern
     */
    public void removePattern(final String pattern) {
        Set<Serializable> keys = redisTemplate.keys(pattern);
        if (keys.size() > 0)
            redisTemplate.delete(keys);
    }

    public void remove(final String key) {
        if (exists(key)) {
            redisTemplate.delete(key);
        }
    }
    
    public boolean exists(final String key) {
        return redisTemplate.hasKey(key);
    }

    public String get(final String key) {
        Object result = null;
        ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
        result = operations.get(key);
        if (result == null) {
            return null;
        }
        return result.toString();
    }
    
    public boolean set(final String key, Object value) {
        boolean result = false;
        try {
            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
            operations.set(key, value);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    public boolean set(final String key, Object value, Long expireTime) {
        boolean result = false;
        try {
            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
            operations.set(key, value);
            redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    public boolean hmset(String key, Map<String, String> value) {
        boolean result = false;
        try {
            redisTemplate.opsForHash().putAll(key, value);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    public Map<String, String> hmget(String key) {
        Map<String, String> result = null;
        try {
            result = redisTemplate.opsForHash().entries(key);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
}

3)StringRedisTemplate

key和value的序列化方式默认就是String方式,省去了我们自定义RedisTemplate的过程。

java 复制代码
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
//  JSON工具
    private static final ObjectMapper mapper = new ObjectMapper();
 
    @Test
    void testStringTemplate() throws JsonProcessingException {
//      准备对象
        User user = new User("abc", 18);
//      手动序列化
        String json = mapper.writeValueAsString(user);
//      写入一条数据到Redis
        stringRedisTemplate.opsForValue().set("user:200",json);
 
//      读取数据
        String s = stringRedisTemplate.opsForValue().get("user:200");
//      反序列化
        User user1 = mapper.readValue(s,User.class);
        System.out.println(user1);
    }

3,Redis数据序列化

4,Repository操作

5,Spring Cache

Spring 3.1 引入了对 Cache 的支持,使用使用 JCache(JSR-107)注解简化开发。

1)org.springframework.cache.Cache接口

  1. 包含了缓存的各种操作集合;
  2. 提供了各种xxxCache 的实现,比如:RedisCache

2)org.springframework.cache.CacheManager接口

  1. 定义了创建、配置、获取、管理和控制多个唯一命名的 Cache。这些 Cache 存在于 CacheManager 的上下文中。
  2. 提供了各种xxxCacheManager 的实现,比如RedisCacheManager。

3)相关注解

1>@EnableCaching

  1. 开启基于注解的缓存;
  2. 作用在缓存配置类上或者SpringBoot 的主启动类上;

2>@Cacheable

缓存注解。

使用注意:

  1. 基于AOP去实现的,所以必须通过IOC对象去调用。
  2. 要缓存的 Java 对象必须实现 Serializable 接口。
java 复制代码
@Cacheable(
            cacheNames = "usersBySpEL",
            //key通过变量拼接
            key="#root.methodName + '[' + #id + ']'",
            //id大于1才缓存。可缺省
            condition = "#id > 1",
            //当id大于10时,条件为true,方法返回值不会被缓存。可缺省
            unless = "#id > 10")
    public User getUserBySpEL(Integer id) {
    }
    
@Cacheable(value = {"menuById"}, key = "'id-' + #menu.id")
    public Menu findById(Menu menu) {
        return menu;
    }
常用属性 说明 备注 代码示例
cacheNames/value 缓存名称,用来划分不同的缓存区,避免相同key值互相影响。 可以是单值、数组; 在redis中相当于key的一级目录,支持:拼接多层目录 cacheNames = "users" cacheNames = {"users","account"}
key 缓存数据时使用的 key,默认是方法参数。 可以使用 spEL 表达式来编写
keyGenerator key 的生成器,统一管理key。 key 和 keyGenerator 二选一使用,同时使用会导致异常。 keyGenerator = "myKeyGenerator"
cacheManager 指定缓存管理器,从哪个缓存管理器里面获取缓存
condition 可以用来指定符合条件的情况下才缓存
unless 否定缓存。 当 unless 指定的条件为 true ,方法的返回值就不会被缓存 通过 #result 获取方法结果进行判断。
sync 是否使用异步模式。 默认是方法执行完,以同步的方式将方法返回的结果存在缓存中

spEL常用元数据:

说明 示例 备注
#root.methodName 当前被调用的方法名
#root.method.name 当前被调用的方法
#root.target 当前被调用的目标对象
#root.targetClass 当前被调用的目标对象类
#root.args[0] 当前被调用的方法的参数列表
#root.cacheds[0].name 当前方法调用使用的缓存区列表
#参数名 或 #p0 或 #a0 方法的参数名; 0代表参数的索引
#result 方法执行后的返回值 如果没有执行则没有内容

3>@CachePut

主要针对配置,能够根据方法的请求参数对其结果进行缓存。

  1. 区别于 @Cacheable,它每次都会触发真实方法的调用,可以保证缓存的一致性。
  2. 属性与 @Cacheable 类同。

4>@CacheEvict

根据一定的条件对缓存进行清空。

  1. 标记在类上时表示其中所有方法的执行都会触发缓存的清除操作;
常用属性 说明 备注 代码示例
value
key
condition
allEntries 为true时,清除value属性值中的所有缓存;默认为false,可以指定清除value属性值下具体某个key的缓存
beforeInvocation 1. 默认是false,即在方法执行成功后触发删除缓存的操作; 2.如果方法抛出异常未能成功返回,不会触发删除缓存的操作 3.当改为true时,方法执行之前会清除指定的缓存,这样不论方法执行成功还是失败都会清除缓存
相关推荐
阿杆13 小时前
为什么我建议你把自建 Redis 迁移到云上进行托管
redis·后端
间彧13 小时前
什么是Redis分布式锁,有何使用场景
redis
间彧17 小时前
Spring Boot项目中,Redis 如何同时执行多条命令
java·redis
Seven9720 小时前
Redis常见性能问题
redis
金銀銅鐵2 天前
Spring 中的 initializeBean 方法的内部逻辑小总结
spring
Seven972 天前
剑指offer-31、整数中1出现的次数
redis
AAA修煤气灶刘哥3 天前
别让Redis「歪脖子」!一次搞定数据倾斜与请求倾斜的捉妖记
redis·分布式·后端
christine-rr4 天前
linux常用命令(4)——压缩命令
linux·服务器·redis
麦兜*4 天前
MongoDB Atlas 云数据库实战:从零搭建全球多节点集群
java·数据库·spring boot·mongodb·spring·spring cloud
麦兜*4 天前
MongoDB 在物联网(IoT)中的应用:海量时序数据处理方案
java·数据库·spring boot·物联网·mongodb·spring