Redis(发布订阅、事务、redis整合springboot、集成 Spring Cache)

目录

一.redis的发布订阅

1、什么 是发布和订阅

2、Redis的发布和订阅

3、发布订阅的代码实现

二.Redis事务

1.事务简介

1、在事务执行之前 如果监听的key的值有变化就不能执行

2、在事务执行之前 如果监听的key的值没有变化就能执行

3、Exec之前就出现错误

4、Exec之后出现的错误

2.redis事务冲突

(1)悲观锁

(2)乐观锁

3.WATCH

三.Redis的使用

1、redis 的 基本 Java 操作

1.1 新建maven项目,导入pom依赖

1.2新建java类,操作redis

2、操作String

 3、操作hash

4、相关API

(1)key的api

(2)string-api

(3)hash-api

(4)set-api

(5)zset-api

(6)list-api

四、redis整合springboot

1、创建springboot项目

2、加入redis的依赖

3、配置yml.properties

4、设置配置类

5、测试代码

(1)String类型常用方法

(2)List类型

(3)Set类型

(4)Hash类型

(5)ZSet类型

 五、集成 Spring Cache

1、@Cacheable

2、value属性指定Cache名称

3、使用key属性自定义key

4、condition属性指定发生的条件

5、@CachePut

6、@CacheEvict

7、 allEntries属性

8、 beforeInvocation属性

六、springboot基于注解的redis缓存 

1、在配置类添加

2、controller添加


一.redis的发布订阅

1、什么 是发布和订阅

Redis 发布订阅 (pub/sub) 是一种消息通信模式:发送者 (pub) 发送消息,订阅者 (sub) 接收消息。

Redis 客户端可以订阅任意数量的频道。

2、Redis的发布和订阅

客户端订阅频道发布的消息

频道发布消息 订阅者就可以收到消息

3、发布订阅的代码实现

(1) 打开一个客户端订阅channel1

subscribe channel1

(2)打开另一个客户端,给channel1发布消息hello

publish channel1 hello

返回的1是订阅者数量

(3)打开第一个客户端可以看到发送的消息

二.Redis事务

1.事务简介

可以一次执行多个命令,本质是一组命令的集合。一个事务中的 所有命令都会序列化,按顺序地串行化执行而不会被其它命令插入,不许加塞。

单独的隔离的操作

官网说明

https://redis.io/docs/interact/transactions/

MULTI、EXEC、DISCARD、WATCH。这四个指令构成了 redis 事务处理的基础。

1.MULTI 用来组装一个事务;将命令存放到一个队列里面

2.EXEC 用来执行一个事务;//commit

3.DISCARD 用来取消一个事务;//rollback

4.WATCH 用来监视一些 key,一旦这些 key 在事务执行之前被改变,则取消事务的执行。

例子:

1、在事务执行之前 如果监听的key的值有变化就不能执行

2、在事务执行之前 如果监听的key的值没有变化就能执行

3、Exec之前就出现错误

执行的时候所有的语句都不执行

4、Exec之后出现的错误

将错误的语句不执行  其他的语句依旧执行

2.redis事务冲突

双十一去购物的时候使用同一张银行卡去付款

10000

一个请求想给金额减8000

一个请求想给金额减5000

一个请求想给金额减1000

解决方案:

(1)悲观锁

select * from biao where 1=1 for update;

悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,

每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,

这样别人想拿这个数据就会block直到它拿到锁。

传统的关系型数据库里边就用到了很多这种锁机制,

比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。

每次操作数据的时候都上锁只操作一个放止发生冲突导致数据发生错误,效率比较低

12306抢票

(2)乐观锁

version 1

查余额 10000 version:1

10000>8000 -8000 update uuuu set moner-=8000 where version=1 1.1

10000 -5000 UPDATE uuuuu SET MONTY-=5000 WHERE VERSION=1

2000 2000>1000 UPDATE uuuu SET MONTY-=1000 WHERE VERSION=1.1 1.2

version

select * from ttt where uid =1

version money

1 10000

乐观锁(Optimistic Lock), 顾名思义,就是很乐观,

每次去拿数据的时候都认为别人不会修改,所以不会上锁,

但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,

可以使用版本号等机制。乐观锁适用于多读的应用类型,

这样可以提高吞吐量。Redis就是利用这种check-and-set机制实现事务的。

version字段代表乐观锁

根据version的字段来操作数据,当三人同时操作同一数据,第一人操作时version就会发生改变

,第二、三的version字段就不再是1,只能重新操作

3.WATCH

“WATCH”可以帮我们实现类似于“乐观锁”的效果,即 CAS(check and set)。

WATCH 本身的作用是“监视 key 是否被改动过”,而且支持同时监视多个 key,只要还没真正触发事务,WATCH 都会尽职尽责的监视,一旦发现某个 key 被修改了,在执行 EXEC 时就会返回 nil,表示事务无法触发。

代码如下:

127.0.0.1:6379> set age 23
OK
127.0.0.1:6379> watch age //开始监视 age
OK
127.0.0.1:6379> set age 24 //在 EXEC 之前,age 的值被修改了
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set age 25
QUEUED
127.0.0.1:6379> get age
QUEUED
127.0.0.1:6379> exec //触发 EXEC
(nil) //事务无法被执行

事务回滚:

192.168.109.34:6379> multi
OK
192.168.109.34:6379> set testsy testq
QUEUED
192.168.109.34:6379> set testsy2 test2
QUEUED
192.168.109.34:6379> discard
OK

三.Redis的使用

1、redis 的 基本 Java 操作

1.1 新建maven项目,导入pom依赖

<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>2.9.0</version>
</dependency>

1.2新建java类,操作redis

package redis;import redis.clients.jedis.Jedis;public class Demo1 {public static void main(String[] args) {
//      建立链接Jedis jedis = new Jedis("1902.168.195.33",6379);//IP地址+端口号
//      设置密码jedis.auth("root");
//      测试链接是否成功System.out.println(jedis.ping());
//        ok
//      String的操作jedis.select(1);//指定数据库jedis.set("toto","才天波");//往指定的数据库中添加数据}
}

或者

public class MyTest {JedisPool pool = null;@BeforeEachpublic void before(){pool = new JedisPool("192.168.195.33",6379);}@Testpublic void testConn(){try (Jedis jedis = pool.getResource()) {jedis.auth("root"); // 设置密码String ping = jedis.ping();System.out.println("测试是不是通:"+ping);}}
}

2、操作String

redis中string的命令

 @Testpublic void testString(){try (Jedis jedis = pool.getResource()) {jedis.auth("root");jedis.set("naem","lc");String s = jedis.get("naem");System.out.println("s="+s);jedis.setex("age",30,"80");}}

 3、操作hash

添加

  @Testpublic void testHash(){try (Jedis jedis = pool.getResource()) {jedis.auth("root");//单个添加jedis.hset("testHash","id","1");jedis.hset("testHash","name","lc");jedis.hset("testHash","age","10");//批量添加}}

批量添加

   @Testpublic void testHash1(){try (Jedis jedis = pool.getResource()) {jedis.auth("root");//批量添加Map map = new HashMap();map.put("testid","1");map.put("testname","lc");map.put("testage","10");map.put("testheidht","199");jedis.hmset("testHashs",map);String hget = jedis.hget("testHashs","testname");System.out.println(hget);}

4、相关API

(1)key的api

 @Testpublic  void testRedis() {//设置连接的服务器 端口号默认是6379// 服务器的默认值是localhostJedis jedis=new Jedis("8.140.27.154"); // redis的服务器的密码jedis.auth("lh"); //设置密码// 设置选中的数据库的下标jedis.select(15);// 设置键值对jedis.set("k1", "v1");jedis.set("k2", "v2");jedis.set("k3", "v3");//获取所有的key值Set<String> keys = jedis.keys("*");System.out.println(keys.size());for (String key : keys) {System.out.println(key);}// 判断key是否存在System.out.println(jedis.exists("k1"));//获取key的过期时间System.out.println(jedis.ttl("k1"));// 获取key对应的值System.out.println(jedis.get("k1"));}}

(2)string-api

// 批量设置
jedis.mset("str1","v1","str2","v2","str3","v3");
//批量获取key
System.out.println(jedis.mget("str1","str2","str3"));

(3)hash-api

//设置 一个key叫做hash1 对应的field是username,value是lisi
jedis.hset("hash1","userName","lisi");
//获取key为hash1的对应的fileld为username的值
System.out.println(jedis.hget("hash1","userName"));
Map<String,String> map = new HashMap<String,String>();
map.put("telphone","13838389438");
map.put("address","郑州");
map.put("email","abc@163.com");
//批量设置
jedis.hmset("hash2",map);
//批量获取
List<String> result = jedis.hmget("hash2", "telphone","email");
for (String element : result) {System.out.println(element);
}

(4)set-api

jedis.sadd("orders", "order01");
jedis.sadd("orders", "order02");
jedis.sadd("orders", "order03");
jedis.sadd("orders", "order04");
Set<String> smembers = jedis.smembers("orders");
for (String order : smembers) {System.out.println(order);
}
//删除集合中的元素
jedis.srem("orders", "order02");

(5)zset-api

jedis.zadd("zset01", 100d, "z3");
jedis.zadd("zset01", 90d, "l4");
jedis.zadd("zset01", 80d, "w5");
jedis.zadd("zset01", 70d, "z6");Set<String> zrange = jedis.zrange("zset01", 0, -1);
for (String e : zrange) {System.out.println(e);
}

(6)list-api

//添加
jedis.lpush("mylist","test1","test2","test3");
//获取list里面的值
List<String> mylist = jedis.lrange("mylist", 0, -1);
for (String s : mylist) {System.out.println(s);
}

四、redis整合springboot

1、创建springboot项目

2、加入redis的依赖

 <!-- springboot连接redsi--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- commons解析包--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency><!--   redis依赖--><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>3.2.0</version></dependency>

3、配置yml.properties

spring:redis:host: 192.168.195.33password: rootdatabase: 1

4、设置配置类

加注解@Configuration

@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {RedisTemplate<String, Object> template = new RedisTemplate<>();RedisSerializer<String> redisSerializer = new StringRedisSerializer();Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);ObjectMapper om = new ObjectMapper();// 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和publicom.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);// 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);template.setConnectionFactory(factory);//key序列化方式template.setKeySerializer(redisSerializer);//value序列化template.setValueSerializer(jackson2JsonRedisSerializer);//value hashmap序列化template.setHashValueSerializer(jackson2JsonRedisSerializer);return template;}

5、测试代码

@SpringBootTest
public class RedisTemplateTest  {@Resourceprivate RedisTemplate redisTemplate;@Testvoid testRdis(){//opsForValue()获取String类型redisTemplate.opsForValue().set("name","ctb");}
}

(1)String类型常用方法

判断是否有key所对应的值,有则返回true,没有则返回false
redisTemplate.hasKey(key)   
有则取出key值所对应的值
redisTemplate.opsForValue().get(key)
redisTemplate.opsForValue().set("key","val")
删除单个key值
redisTemplate.delete(key)
批量删除key
redisTemplate.delete(keys) //其中keys:Collection<K> keysvoid set(K key, V value);redisTemplate.opsForValue().set("num","123");redisTemplate.opsForValue().get("num")  输出结果为123void set(K key, V value, long timeout, TimeUnit unit); redisTemplate.opsForValue().set("num","123",10, TimeUnit.SECONDS);redisTemplate.opsForValue().get("num")设置的是10秒失效,十秒之内查询有结果,十秒之后返回为nullTimeUnit.DAYS          //天TimeUnit.HOURS         //小时TimeUnit.MINUTES       //分钟TimeUnit.SECONDS       //秒TimeUnit.MILLISECONDS  //毫秒 void set(K key, V value, long offset);覆写(overwrite)给定 key 所储存的字符串值,从偏移量 offset 开始template.opsForValue().set("key","hello world");template.opsForValue().set("key","redis", 6);System.out.println("***************"+template.opsForValue().get("key"));结果:***************hello redisV get(Object key);template.opsForValue().set("key","hello world");System.out.println("***************"+template.opsForValue().get("key"));结果:***************hello worldgetAndSet V getAndSet(K key, V value); 设置键的字符串值并返回其旧值template.opsForValue().set("getSetTest","test");System.out.println(template.opsForValue().getAndSet("getSetTest","test2"));结果:testappend Integer append(K key, String value);如果key已经存在并且是一个字符串,则该命令将该值追加到字符串的末尾。如果键不存在,则它被创建并设置为空字符串,因此APPEND在这种特殊情况下将类似于SET。template.opsForValue().append("test","Hello");System.out.println(template.opsForValue().get("test"));template.opsForValue().append("test","world");System.out.println(template.opsForValue().get("test"));HelloHelloworldsize Long size(K key);
返回key所对应的value值得长度template.opsForValue().set("key","hello world");System.out.println("***************"+template.opsForValue().size("key"));***************11

(2)List类型

通过索引获取列表中的元素
redisTemplate.opsForList().index(key, index)获取列表指定范围内的元素(start开始位置, 0是开始位置,end 结束位置, -1返回所有)
redisTemplate.opsForList().range(key, start, end)存储在list的头部,即添加一个就把它放在最前面的索引处
redisTemplate.opsForList().leftPush(key, value)  把多个值存入List中(value可以是多个值,也可以是一个Collection value)
redisTemplate.opsForList().leftPushAll(key, value)List存在的时候再加入
redisTemplate.opsForList().leftPushIfPresent(key, value)如果pivot处值存在则在pivot前面添加
redisTemplate.opsForList().leftPush(key, pivot, value)按照先进先出的顺序来添加(value可以是多个值,或者是Collection var2)
redisTemplate.opsForList().rightPush(key, value)redisTemplate.opsForList().rightPushAll(key, value)
在pivot元素的右边添加值redisTemplate.opsForList().rightPush(key, pivot, value)设置指定索引处元素的值redisTemplate.opsForList().set(key, index, value)移除并获取列表中第一个元素(如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止)redisTemplate.opsForList().leftPop(key)redisTemplate.opsForList().leftPop(key, timeout, unit)移除并获取列表最后一个元素redisTemplate.opsForList().rightPop(key)redisTemplate.opsForList().rightPop(key, timeout, unit)
从一个队列的右边弹出一个元素并将这个元素放入另一个指定队列的最左边redisTemplate.opsForList().rightPopAndLeftPush(sourceKey, destinationKey)redisTemplate.opsForList().rightPopAndLeftPush(sourceKey, destinationKey, timeout, unit)删除集合中值等于value的元素(index=0, 删除所有值等于value的元素; index>0, 从头部开始删除第一个值等于value的元素; index<0, 从尾部开始删除第一个值等于value的元素)redisTemplate.opsForList().remove(key, index, value)将List列表进行剪裁redisTemplate.opsForList().trim(key, start, end)获取当前key的List列表长度
redisTemplate.opsForList().size(key)

(3)Set类型

添加元素
redisTemplate.opsForSet().add(key, values)
移除元素(单个值、多个值)
redisTemplate.opsForSet().remove(key, values)删除并且返回一个随机的元素redisTemplate.opsForSet().pop(key)获取集合的大小redisTemplate.opsForSet().size(key)判断集合是否包含value
redisTemplate.opsForSet().isMember(key, value)获取两个集合的交集(key对应的无序集合与otherKey对应的无序集合求交集)
redisTemplate.opsForSet().intersect(key, otherKey)获取多个集合的交集(Collection var2)redisTemplate.opsForSet().intersect(key, otherKeys)key集合与otherKey集合的交集存储到destKey集合中(其中otherKey可以为单个值或者集合)redisTemplate.opsForSet().intersectAndStore(key, otherKey, destKey)key集合与多个集合的交集存储到destKey无序集合中redisTemplate.opsForSet().intersectAndStore(key, otherKeys, destKey)获取两个或者多个集合的并集(otherKeys可以为单个值或者是集合)redisTemplate.opsForSet().union(key, otherKeys)key集合与otherKey集合的并集存储到destKey中(otherKeys可以为单个值或者是集合)redisTemplate.opsForSet().unionAndStore(key, otherKey, destKey)获取两个或者多个集合的差集(otherKeys可以为单个值或者是集合)redisTemplate.opsForSet().difference(key, otherKeys)差集存储到destKey中(otherKeys可以为单个值或者集合)redisTemplate.opsForSet().differenceAndStore(key, otherKey, destKey)随机获取集合中的一个元素redisTemplate.opsForSet().randomMember(key)获取集合中的所有元素redisTemplate.opsForSet().members(key)随机获取集合中count个元素redisTemplate.opsForSet().randomMembers(key, count)获取多个key无序集合中的元素(去重),count表示个数redisTemplate.opsForSet().distinctRandomMembers(key, count)遍历set类似于Interator(ScanOptions.NONE为显示所有的)redisTemplate.opsForSet().scan(key, options)

(4)Hash类型

Long delete(H key, Object... hashKeys);
删除给定的哈希hashKeysSystem.out.println(template.opsForHash().delete("redisHash","name"));System.out.println(template.opsForHash().entries("redisHash"));1{class=6, age=28.1}Boolean hasKey(H key, Object hashKey);
确定哈希hashKey是否存在System.out.println(template.opsForHash().hasKey("redisHash","666"));System.out.println(template.opsForHash().hasKey("redisHash","777"));truefalseHV get(H key, Object hashKey);
从键中的哈希获取给定hashKey的值System.out.println(template.opsForHash().get("redisHash","age"));26Set<HK> keys(H key);
获取key所对应的散列表的keySystem.out.println(template.opsForHash().keys("redisHash"));//redisHash所对应的散列表为{class=1, name=666, age=27}[name, class, age]Long size(H key);
获取key所对应的散列表的大小个数System.out.println(template.opsForHash().size("redisHash"));//redisHash所对应的散列表为{class=1, name=666, age=27}3void putAll(H key, Map<? extends HK, ? extends HV> m);
使用m中提供的多个散列字段设置到key对应的散列表中Map<String,Object> testMap = new HashMap();testMap.put("name","666");testMap.put("age",27);testMap.put("class","1");template.opsForHash().putAll("redisHash1",testMap);System.out.println(template.opsForHash().entries("redisHash1"));{class=1, name=jack, age=27}void put(H key, HK hashKey, HV value);
设置散列hashKey的值template.opsForHash().put("redisHash","name","666");template.opsForHash().put("redisHash","age",26);template.opsForHash().put("redisHash","class","6");System.out.println(template.opsForHash().entries("redisHash"));{age=26, class=6, name=666}List<HV> values(H key);
获取整个哈希存储的值根据密钥System.out.println(template.opsForHash().values("redisHash"));[tom, 26, 6]Map<HK, HV> entries(H key);
获取整个哈希存储根据密钥System.out.println(template.opsForHash().entries("redisHash"));{age=26, class=6, name=tom}Cursor<Map.Entry<HK, HV>> scan(H key, ScanOptions options);
使用Cursor在key的hash中迭代,相当于迭代器。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());}age:27class:6name:666

命令

操作

返回值

hash.delete(H key, Object... hashKeys)

删除,可以传入多个map的key【hdel】

Long

hash.hasKey(key, hashKey)

查看hash中的某个hashKey是否存在【hexists】

Boolean

hash.get(key, hashKey)

获取值【hget】

Object(HV 泛型约束对象)

hash.multiGet(H key, Collection<HK> hashKeys)

批量获取集合中的key对应的值【hmget】

List<HV>

hash.increment(H key, HK hashKey, long delta)

对值进行+(delta值)操作【】

Long

hash.increment(H key, HK hashKey, double delta)

~

double

hash.keys(key)

返回map内hashKey的集合【hkeys】

Set<HK>

hash.lengthOfValue(H key, HK hashKey)

返回查询键关联的值的长度,为null则返回0【hstrlen】

Long

hash.size(H key)

获取hashKey的个数【hlen】

Long

hash.putAll(H key, Map<? extends HK, ? extends HV> m)

相当于map的putAll【hmset】

void

hash.put(H key, HK hashKey, HV value)

设置值,添加hashKey-value,hashKay相当于map的key 【hset】

void

hash.putIfAbsent(H key, HK hashKey, HV value)

仅当hashKey不存在时设置值

Boolean

hash.values(key)

返回value的集合【hvals】

List<HV>

hase.entries(H key)

获取map【hgetall】

Map<HK, HV>

hash.scan(H key, ScanOptions options)

基于游标的迭代查询【hscan】

Cursor<Map.Entry<HK, HV>>(返回的Cursor要手动关闭,见下面示例2)

hash.getOperations()

返回RedisOperation,它就是redis操作的接口

RedisOperations<H, ?>

(5)ZSet类型

Boolean add(K key, V value, double score);
新增一个有序集合,存在的话为false,不存在的话为trueSystem.out.println(template.opsForZSet().add("zset1","zset-1",1.0));trueLong add(K key, Set<TypedTuple<V>> tuples);
新增一个有序集合ZSetOperations.TypedTuple<Object> objectTypedTuple1 = new DefaultTypedTuple<>("zset-5",9.6);ZSetOperations.TypedTuple<Object> objectTypedTuple2 = new DefaultTypedTuple<>("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));[zset-1, zset-2, zset-3, zset-4, zset-5, zset-6]
Long remove(K key, Object... values);
从有序集合中移除一个或者多个元素System.out.println(template.opsForZSet().range("zset1",0,-1));System.out.println(template.opsForZSet().remove("zset1","zset-6"));System.out.println(template.opsForZSet().range("zset1",0,-1));[zset-1, zset-2, zset-3, zset-4, zset-5, zset-6]1[zset-1, zset-2, zset-3, zset-4, zset-5]Long rank(K key, Object o);
返回有序集中指定成员的排名,其中有序集成员按分数值递增(从小到大)顺序排列System.out.println(template.opsForZSet().range("zset1",0,-1));System.out.println(template.opsForZSet().rank("zset1","zset-2"));[zset-2, zset-1, zset-3, zset-4, zset-5]0   //表明排名第一Set<V> range(K key, long start, long end);
通过索引区间返回有序集合成指定区间内的成员,其中有序集成员按分数值递增(从小到大)顺序排列System.out.println(template.opsForZSet().range("zset1",0,-1));[zset-2, zset-1, zset-3, zset-4, zset-5]
Long count(K key, double min, double max);
通过分数返回有序集合指定区间内的成员个数System.out.println(template.opsForZSet().rangeByScore("zset1",0,5));System.out.println(template.opsForZSet().count("zset1",0,5));[zset-2, zset-1, zset-3]3Long size(K key);
获取有序集合的成员数,内部调用的就是zCard方法System.out.println(template.opsForZSet().size("zset1"));6Double score(K key, Object o);
获取指定成员的score值System.out.println(template.opsForZSet().score("zset1","zset-1"));2.2Long removeRange(K key, long start, long end);
移除指定索引位置的成员,其中有序集成员按分数值递增(从小到大)顺序排列System.out.println(template.opsForZSet().range("zset2",0,-1));System.out.println(template.opsForZSet().removeRange("zset2",1,2));System.out.println(template.opsForZSet().range("zset2",0,-1));[zset-1, zset-2, zset-3, zset-4]2[zset-1, zset-4]Cursor<TypedTuple<V>> scan(K key, ScanOptions options);
遍历zsetCursor<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());}zset-1:1.0zset-2:2.0zset-3:3.0zset-4:6.0

 五、集成 Spring Cache

1、@Cacheable

@Cacheable可以标记在一个方法上,也可以标记在一个类上。

当标记在一个方法上时表示该方法是支持缓存的,当标记在一个类上时则表示该类所有的方法都是支持缓存的。对于一个支持缓存的方法,Spring会在其被调用后将其返回值缓存起来,以保证下次利用同样的参数来执行该方法时可以直接从缓存中获取结果,而不需要再次执行该方法。

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

@Cacheable可以指定三个属性,value、key和condition。

参数

解释

example

value

缓存的名称,在 spring 配置文件中定义,必须指定至少一个

例如:

@Cacheable(value=”mycache”)

@Cacheable(value={”cache1”,”cache2”}

key

缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合

@Cacheable(value=”testcache”,key=”#userName”)

condition

缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存

@Cacheable(value=”testcache”,condition=”#userName.length()>2”)

2、value属性指定Cache名称

value其表示当前方法的返回值是会被缓存在哪个Cache上的,对应Cache的名称。其可以是一个Cache也可以是多个Cache,当需要指定多个Cache时其是一个数组。

   @Cacheable("cache1")//Cache是发生在cache1上的public User find(Integer id) {returnnull;}@Cacheable({"cache1", "cache2"})//Cache是发生在cache1和cache2上的public User find(Integer id) {returnnull;}

3、使用key属性自定义key

key属性是用来指定Spring缓存方法的返回结果时对应的key的。该属性支持SpringEL表达式。当我们没有指定该属性时,Spring将使用默认策略生成key。我们这里先来看看自定义策略

自定义策略是指我们可以通过Spring的EL表达式来指定我们的key。这里的EL表达式可以使用方法参数及它们对应的属性。使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”。下面是几个使用参数作为key的示例。

   @Cacheable(value="users", key="#id")public User find(Integer id) {return  null;}// p  param 参数   0 @Cacheable(value="users", key="#p0")public User find(Integer id) {returnnull;}@Cacheable(value="users", key="#user.id")public User find(User user) {returnnull;}@Cacheable(value="users", key="#p0.id")public User find(User user) {returnnull;}

除了上述使用方法参数作为key之外,Spring还为我们提供了一个root对象可以用来生成key。通过该root对象我们可以获取到以下信息。

属性名称

描述

示例

methodName

当前方法名

#root.methodName

method

当前方法

#root.method.name

target

当前被调用的对象

#root.target

targetClass

当前被调用的对象的class

#root.targetClass

args

当前方法参数组成的数组

#root.args[0]

caches

当前被调用的方法使用的Cache

#root.caches[0].name

当我们要使用root对象的属性作为key时我们也可以将“#root”省略,因为Spring默认使用的就是root对象的属性。如:

   @Cacheable(value={"users", "xxx"}, key="caches[1].name")public User find(User user) {returnnull;}

4、condition属性指定发生的条件

有的时候我们可能并不希望缓存一个方法所有的返回结果。通过condition属性可以实现这一功能。condition属性默认为空,表示将缓存所有的调用情形。其值是通过SpringEL表达式来指定的,当为true时表示进行缓存处理;当为false时表示不进行缓存处理,即每次调用该方法时该方法都会执行一次。如下示例表示只有当user的id为偶数时才会进行缓存。

   @Cacheable(value={"users"}, key="#user.id", condition="#user.id%2==0")public User find(User user) {System.out.println("find user by user " + user);return user;}

5、@CachePut

在支持Spring Cache的环境下,对于使用@Cacheable标注的方法,Spring在每次执行前都会检查Cache中是否存在相同key的缓存元素,如果存在就不再执行该方法,而是直接从缓存中获取结果进行返回,否则才会执行并将返回结果存入指定的缓存中。@CachePut也可以声明一个方法支持缓存功能。与@Cacheable不同的是使用@CachePut标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中。

一般使用在保存,更新方法中。

@CachePut也可以标注在类上和方法上。使用@CachePut时我们可以指定的属性跟 @Cacheable是一样的。

    @Cacheable(cacheNames = "user", key = "#id")//每次都会执行方法,并将结果存入指定的缓存中public User find(Integer id) {return null;}

6、@CacheEvict

@CacheEvict是用来标注在需要清除缓存元素的方法或类上的。当标记在一个类上时表示其中所有的方法的执行都会触发缓存的清除操作。@CacheEvict可以指定的属性有value、key、condition、allEntries和beforeInvocation。其中value、key和condition的语义与@Cacheable对应的属性类似。即value表示清除操作是发生在哪些Cache上的(对应Cache的名称);key表示需要清除的是哪个key,如未指定则会使用默认策略生成的key;condition表示清除操作发生的条件。下面我们来介绍一下新出现的两个属性allEntries和beforeInvocation。

7、 allEntries属性

allEntries是boolean类型,表示是否需要清除缓存中的所有元素。默认为false,表示不需要。当指定了allEntries为true时,Spring Cache将忽略指定的key。有的时候我们需要Cache一下清除所有的元素,这比一个一个清除元素更有效率。

@CacheEvict(value="users",key = "#id", allEntries=true)
public void delete(Integer id) {System.out.println("delete user by id: " + id);
}

8、 beforeInvocation属性

清除操作默认是在对应方法成功执行之后触发的,即方法如果因为抛出异常而未能成功返回时也不会触发清除操作。使用beforeInvocation可以改变触发清除操作的时间,当我们指定该属性值为true时,Spring会在调用该方法之前清除缓存中的指定元素。

@CacheEvict(value="users",key = "#id", beforeInvocation=true)
public void delete(Integer id) {System.out.println("delete user by id: " + id);
}

六、springboot基于注解的redis缓存 

 此项目是在已搭配好的springboot项目基础完成

1、在配置类添加

  /*** 缓存处理* @param factory* @return*/@Beanpublic CacheManager cacheManager(RedisConnectionFactory factory) {RedisSerializer<String> redisSerializer = new StringRedisSerializer();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);
// 配置序列化(解决乱码的问题),过期时间600秒RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(600)).serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer)).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)).disableCachingNullValues();RedisCacheManager cacheManager = RedisCacheManager.builder(factory).cacheDefaults(config).build();return cacheManager;}

并在配置类中加入@EnableCaching 启动缓存

2、controller添加

@RestController
@RequestMapping("/dept")
public class DeptController {@Resourceprivate IDeptService deptService;@GetMapping@Cacheable(value = "mytest")
//@Cacheable 的作用 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存
//@Cacheable可以指定三个属性,value、key和condition。public ResponseVo findAll(){return ResponseVo.SUCCESS(deptService.Mylist());}

 @Cacheable(value = "mytest")  查询的时候缓存,

Value  指定         Key            Condition  条件

serviceimpl

service接口

启动后访问dept第二次就直接访问redis缓存

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/643574.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Centos7 两种方式安装 MySQL5.7 步骤 yum 、本地 tar 文件

一、使用 yum 源方式安装 1、卸载系统自带 mariadb MariaDB Server 是最流行的开源 关系型数据库 之一。它由 MySQL 的原始开发者制作&#xff0c;并保证保持开源。 在 CentOS 7 中默认安装有 MariaDB 可忽略&#xff0c;安装完成之后可以直接覆盖掉 MariaDB。 查看并卸载系统…

【极数系列】Flink项目入门搭建(03)

【极数系列】Flink项目入门搭建&#xff08;03&#xff09; 引言 gitee地址&#xff1a;https://gitee.com/shawsongyue/aurora.git 源码直接下载可运行&#xff0c;模块&#xff1a;aurora_flink Flink 版本&#xff1a;1.18.0 Jdk 版本&#xff1a;11 1.创建mavenx项目 2.…

清越 peropure·AI 国内版ChatGP新功能介绍

当OpenAI发布ChatGPT的时候,没有人会意识到,新一代人工智能浪潮将给人类社会带来一场眩晕式变革。其中以ChatGPT为代表的AIGC技术加速成为AI领域的热门发展方向,推动着AI时代的前行发展。面对技术浪潮,清越科技(PeroPure)立足多样化生活场景、精准把握用户实际需求,持续精确Fin…

SpringBoot+Vue充电桩管理系统 附带详细运行指导视频

文章目录 一、项目演示二、项目介绍三、运行截图四、主要代码1. 分页获取预约数据代码2.保存预约信息代码3.修改订单状态代码 一、项目演示 项目演示地址&#xff1a; 视频地址 二、项目介绍 项目描述&#xff1a;这是一个基于SpringBootVue框架开发的充电桩管理系统。首先&…

网络安全的使命:守护数字世界的稳定和信任

在数字化时代&#xff0c;网络安全的角色不仅仅是技术系统的守护者&#xff0c;更是数字社会的信任保卫者。网络安全的使命是保护、维护和巩固数字世界的稳定性、可靠性以及人们对互联网的信任。本文将深入探讨网络安全是如何履行这一使命的。 第一部分&#xff1a;信息资产的…

怎么给wordpress网站底部页脚添加备案号和链接?

以前“WordPress后台 >> 常规”最底部是有一个ICP备案号的&#xff0c;我们只需要填写备案号并保存更改即可让WordPress自带主题底部显示ICP备案号&#xff0c;但是现在新版本的WordPress已经没有了这个ICP备案号选项&#xff0c;而且也无法直接添加公安联网备案号&#…

Typecho后台无法登录显示503 service unavailable问题及处理

一、Typecho 我的博客地址&#xff1a;https://www.aomanhao.top 使用老薛主机动态Typecho博客框架handsome主题的搭配&#xff0c;文章内容可以异地网页更新&#xff0c;可以听后台背景音乐&#xff0c;很好的满足我的痛点需求&#xff0c;博客部署在云端服务器访问响应较快…

单调栈笔记

单调栈 1.每日温度2.下一个更大元素 I3.下一个更大的元素4.接雨水5.柱状图中最大的矩形 单调栈正如其名字&#xff0c;用一个栈&#xff08;能够实现栈性质的数据结构就行&#xff09;来存储元素&#xff0c;存储在栈中的元素保持单调性&#xff08;单调递增或者是单调递减&…

信息检索与数据挖掘 | (十)线性回归与逻辑回归

文章目录 &#x1f4da;线性回归算法流程&#x1f4da;Bias and variance&#x1f4da;过拟合&欠拟合&#x1f4da;逻辑回归算法流程 &#x1f4da;线性回归算法流程 ybwx 使用loss function L来评估函数的好坏 从而我们要选择使L最小的模型参数w,b 使用梯度下降的方法…

JAVA 学习 面试(六)数据类型与方法

数据类型 基本数据类型 为什么float3.4报错 3.4 默认是浮点double类型的&#xff0c;如果赋值给float是向下转型&#xff0c;会出现精度缺失&#xff0c;&#xff0c;需要强制转换 Switch支持的数据类型&#xff1f; byte、short、int、char 、 enum 、 String 基本类型与包…

trino-435: trino接入TIDB数据源

文章目录 一、TIDB介绍二、TIDB源接入流程三、遇到的错误1、数据源注册:2、查询表数据一、TIDB介绍 二、TIDB源接入流程 三、遇到的错误 1、数据源注册: http://localhost:8080/v1/catalog/register?name=tidb_test {"connector.name":"tidb",&quo…

SpringBoot整合QQ邮箱发送验证码

一、QQ开启SMTP 打开QQ邮箱&#xff0c;点击设置&#xff0c;进入账号&#xff0c;往下滑后&#xff0c;看见服务状态后&#xff0c;点击管理服务 进入管理服务后&#xff0c;打开服务&#xff0c;然后获取授权码 二 、导入依赖 <!-- 邮箱--><dependency>&…

php低版本(7.4)配置过程中遇到的问题及基本解决手段

目前php不支持较低版本的安装&#xff0c;如果安装低版本必须借助第三方库shivammathur //将第三方仓库加入brewbrew tap shivammathur/php //安装PHPbrew install shivammathur/php/php7.4 可能出现的问题 像这样突然中止然后报错&#xff0c;一般是网络问题&#xff0c;或…

Random Sprays Retinex 传统的图像增强算法RSR

文章目录 前言1、Random Sprays Retinex 概况2、Random Sprays Retinex 具体实现2.1、喷雾的生成2.2、径向密度函数的确定2.3、像素的选择2.4、亮度的计算2.5、参数的调整 3、Random Sprays Retinex 算法效果的表现4、Random Sprays Retinex 现存的问题 前言 Random Spray Reti…

C++面试宝典第24题:袋鼠过河

题目 一只袋鼠要从河这边跳到河对岸,河很宽,但是河中间打了很多桩子。每隔一米就有一个桩子,每个桩子上都有一个弹簧,袋鼠跳到弹簧上就可以跳得更远。每个弹簧力量不同,用一个数字代表它的力量,如果弹簧力量为5,就代表袋鼠下一跳最多能够跳5米;如果为0,就会陷进去无法…

代码中遇到的问题2

目录 记录: 好处&#xff1a; 问题一&#xff1a; 解答: 问题二: 解答: 常见类型问题: 记录: string connStr ConfigurationManager.ConnectionStrings["connStr"].ConnectionString; 在代码中写上这段代码&#xff0c;将连接数据库的代码语句放到&#xff…

关于在微信小程序中使用taro + react-hook后销毁函数无法执行的问题

问题&#xff1a; 在 taro中使用navigageTo() 跳转路由后hook中useEffect 的return函数没有执行 没有执行return函数 框架版本&#xff1a; tarojs: 3.6 react: 18.0 原因&#xff1a; 使用navigateTo() 跳转路由的话并不会销毁页面和组件&#xff0c;会加入一…

性能优化-OpenCL运行时API介绍

「发表于知乎专栏《移动端算法优化》」 本文首先给出 OpenCL 运行时 API 的整体编程流程图&#xff0c;然后针对每一步介绍使用的运行时 API&#xff0c;讲解 API 参数&#xff0c;并给出编程运行实例。总结运行时 API 使用的注意事项。最后展示基于 OpenCL 的图像转置代码。在…

L1-093 猜帽子游戏(Java)

宝宝们在一起玩一个猜帽子游戏。每人头上被扣了一顶帽子&#xff0c;有的是黑色的&#xff0c;有的是黄色的。每个人可以看到别人头上的帽子&#xff0c;但是看不到自己的。游戏开始后&#xff0c;每个人可以猜自己头上的帽子是什么颜色&#xff0c;或者可以弃权不猜。如果没有…

17β-Estradiol high sensitivity ELISA kit

高灵敏ELISA试剂盒&#xff0c;可检测到低至14 pg/ml的17β-雌二醇 雌二醇(estradiol) 是由卵巢内卵泡的颗粒细胞分泌的类固醇激素&#xff0c;是主要的雌激素&#xff0c;负责调节女性特征、附属性器官的成熟和月经-排卵周期&#xff0c;促进乳腺导管系统的产生&#xff0c;有…