一、配置redis,防止乱码
package com.duanmh.config.redis;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import org.springframework.util.Assert;
import java.nio.charset.Charset;
/**
* Redis使用FastJson序列化
* @Author dmh 2022/4/11
*/
public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T> {
@SuppressWarnings("unused")
private ObjectMapper objectMapper = new ObjectMapper();
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private Class<T> clazz;
public FastJson2JsonRedisSerializer(Class<T> clazz)
{
super();
this.clazz = clazz;
}
@Override
public byte[] serialize(T t) throws SerializationException
{
if (t == null)
{
return new byte[0];
}
return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
}
@Override
public T deserialize(byte[] bytes) throws SerializationException
{
if (bytes == null || bytes.length <= 0)
{
return null;
}
String str = new String(bytes, DEFAULT_CHARSET);
return JSON.parseObject(str, clazz);
}
public void setObjectMapper(ObjectMapper objectMapper)
{
Assert.notNull(objectMapper, "'objectMapper' must not be null");
this.objectMapper = objectMapper;
}
protected JavaType getJavaType(Class<?> clazz)
{
return TypeFactory.defaultInstance().constructType(clazz);
}
}
package com.duanmh.config.redis;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.SingleServerConfig;
import org.springframework.beans.factory.annotation.Value;
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.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/** redis配置 @Author dmh 2022/4/11 */
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
@Value(value = "${spring.redis.host}")
private String host;
@Value(value = "${spring.redis.port}")
private int port;
/* @Value(value = "${spring.redis.database}")
private int database;
@Value(value = "${spring.redis.password}")
private String password;*/
/**
* 单Redis节点模式配置方法
* 其他配置參數,看:
* <a href = "https://github.com/redisson/redisson/wiki/2.-%E9%85%8D%E7%BD%AE%E6%96%B9%E6%B3%95#26-%E5%8D%95redis%E8%8A%82%E7%82%B9%E6%A8%A1%E5%BC%8F">
* 单Redis节点模式配置方法
* </a>
*
* @return {@link RedissonClient}
*/
@Bean(destroyMethod = "shutdown")
RedissonClient redisson() {
Config config = new Config();
//Redis多节点
// config.useClusterServers()
// .addNodeAddress("redis://127.0.0.1:6379", "redis://127.0.0.1:7001");
//Redis单节点
SingleServerConfig singleServerConfig = config.useSingleServer();
//可以用"rediss://"来启用SSL连接
String address = "redis://" + host + ":" + port;
singleServerConfig.setAddress(address);
//设置 数据库编号
/*singleServerConfig.setDatabase(database);
singleServerConfig.setPassword(password);*/
//连接池大小:默认值:64
// singleServerConfig.setConnectionPoolSize()
return Redisson.create(config);
}
/**
* 设置redis编码信息,防止乱码
* @param connectionFactory
* @return
*/
@Bean
@SuppressWarnings(value = {"unchecked", "rawtypes"})
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.activateDefaultTyping(
LaissezFaireSubTypeValidator.instance,
ObjectMapper.DefaultTyping.NON_FINAL,
JsonTypeInfo.As.PROPERTY);
serializer.setObjectMapper(mapper);
// 使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(serializer);
// Hash的key也采用StringRedisSerializer的序列化方式
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(serializer);
template.afterPropertiesSet();
return template;
}
@Bean
public DefaultRedisScript<Long> limitScript() {
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
redisScript.setScriptText(limitScriptText());
redisScript.setResultType(Long.class);
return redisScript;
}
/** 限流脚本 */
private String limitScriptText() {
return "local key = KEYS[1]\n"
+ "local count = tonumber(ARGV[1])\n"
+ "local time = tonumber(ARGV[2])\n"
+ "local current = redis.call('get', key);\n"
+ "if current and tonumber(current) > count then\n"
+ " return tonumber(current);\n"
+ "end\n"
+ "current = redis.call('incr', key)\n"
+ "if tonumber(current) == 1 then\n"
+ " redis.call('expire', key, time)\n"
+ "end\n"
+ "return tonumber(current);";
}
}
二、使用redisTemplate操作各种类型数据
import com.duanmh.PublisherApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
@RunWith(SpringRunner.class)
@SpringBootTest(classes= PublisherApplication.class)
public class RedisTemplateTest {
@Resource
RedisTemplate redisTemplate;
/**
* 使用RedisTemplate 操作 String
*
* 应用场景 :
* 存储字符串,常用于缓存(常用不常改变的数据),计数器(如库存)和分布式锁
*/
@Test
public void StringTemplate() throws InterruptedException {
//将字符串直接存入Redis
redisTemplate.opsForValue().set("userName","张三");
//将字符串读取出来
String massage = (String) redisTemplate.opsForValue().get("userName");
System.out.println("读取直接存入redisTemplate数据:{}"+massage);
//同时添加多个Key和Val
Map<String, Object> hashMap = new HashMap<>();
hashMap.put("a", "123");
hashMap.put("b","456");
redisTemplate.opsForValue().multiSet(hashMap);
//读取多个key
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
List<String> multiGet = redisTemplate.opsForValue().multiGet(list);
for (String key : multiGet) {
System.out.println("读取批量存入redisTemplate数据:{}"+key);
}
//将数据存入Redis,有效期3秒
redisTemplate.opsForValue().set("name", "李四",3,TimeUnit.SECONDS);
String name = (String)redisTemplate.opsForValue().get("name");
System.out.println("读取在有效期的数据:{}"+name);
Thread.sleep(4000);
String name2 = (String)redisTemplate.opsForValue().get("name");
System.out.println("读取未在有效期的数据:{}"+name2);
//读取之后重新赋值
String andSet = (String)redisTemplate.opsForValue().getAndSet("userName", "大春");
System.out.println("修改前的userName:{}"+andSet);
String userName = (String)redisTemplate.opsForValue().get("userName");
System.out.println("修改后的userName:{}"+userName);
//增减值
Long age1 = redisTemplate.opsForValue().increment("age", 2);
System.out.println("加上两年的年龄:{}"+ age1);
Long age2 = redisTemplate.opsForValue().decrement("age", 1);
System.out.println("减去一年的年龄:{}"+ age2);
Long age = redisTemplate.opsForValue().increment("age", 10);
System.out.println("加上十年的年龄:{}"+age);
}
/**
* 使用RedisTemplate操作 List
*
* 使用场景:
* list的特点是可以从头或尾存入或读取数据,所以可以用于消息队列、最新消息、过段时间计算一次的排行等场景
*/
@Test
public void ListTempalate(){
//直接从头加入条数据
Long list = redisTemplate.opsForList().leftPush("list", 3);
//批量从头加入一条数据
List<Integer> ints = new ArrayList<>();
ints.add(2);
ints.add(1);
Long list2 = redisTemplate.opsForList().leftPushAll("list", ints);
List all1 = redisTemplate.opsForList().range("list", 0, -1);
System.out.println("从头加入数据为:{}"+ all1);
//直接从尾加入一条数据
Long list3 = redisTemplate.opsForList().rightPush("list", "a");
//批量从尾加入一条数据
List<String> ints2 = new ArrayList<>();
ints2.add("b");
ints2.add("c");
redisTemplate.opsForList().rightPushAll("list", ints2);
List all2 = redisTemplate.opsForList().range("list", 0, -1);
System.out.println("从尾部加入数据为:{}"+ all2);
//从左侧读取一个并删除
List list1 = redisTemplate.opsForList().leftPop("list", 1);
List all3 = redisTemplate.opsForList().range("list", 0, -1);
System.out.println("从左侧读取一个并删除数据为:{}"+ all3);
//从右侧读取一个并删除
List list5 = redisTemplate.opsForList().rightPop("list", 1);
List all4 = redisTemplate.opsForList().range("list", 0, -1);
System.out.println("从右侧读取一个并删除数据为:{}"+ all4);
//读取全部list
List all5 = redisTemplate.opsForList().range("list", 0, -1);
System.out.println("最后的数据为 :{}"+all5);
}
/**
* 使用RedisTemplate操作 set
*
* 使用场景:
* set的特点是无序不可重复, 可以用于去重、抽奖、共同好友等场景
*
*/
@Test
public void setTemplate(){
//添加三个参数
String []arr={"a","1","2","3"};
Long set = redisTemplate.opsForSet().add("set", arr);
//取出数据
Set set1 = redisTemplate.opsForSet().members("set");
System.out.println("直接存入set的数据为:{}"+set1);
//删除数据
redisTemplate.opsForSet().remove("set","a");
Set set2 = redisTemplate.opsForSet().members("set");
System.out.println("删除a之后的数据:{}"+set2);
//获取随机数据,但是不删除元素(可能会重复)
List set3 = redisTemplate.opsForSet().randomMembers("set", 2);
System.out.println("随机获取两个数据:{}"+set3);
//在添加一条数据
String []arr1={"3","4","5"};
redisTemplate.opsForSet().add("set1",arr1);
Set set11 = redisTemplate.opsForSet().members("set1");
System.out.println("set1的数据为:{}"+set11);
//用法5:计算出set和set1的差运算值
Set difference = redisTemplate.opsForSet().difference("set", "set1");
System.out.println("set和set1的差集:{}"+difference);
//用法6:将test和test1的差值存入test2中
redisTemplate.opsForSet().differenceAndStore("set","set1","difference");
Set set21 = redisTemplate.opsForSet().members("difference");
System.out.println("set2中存入的set和set1的差集:{}"+set21);
//用法7:计算test和test1的交集
Set intersect = redisTemplate.opsForSet().intersect("set", "set1");
System.out.println("set和set1的交集:{}"+intersect);
//用法8:将test和test1的交集存入test2中
redisTemplate.opsForSet().intersectAndStore("set","set1","intersect");
Set intersect1 = redisTemplate.opsForSet().members("intersect");
System.out.println("set和set1存起来的交集:{}"+intersect1);
//用法9:计算test和test1的并集
Set union = redisTemplate.opsForSet().union("set", "set1");
System.out.println("set和set1的并集:{}"+union);
//用法10:将test和test1的并集存入test2中
redisTemplate.opsForSet().unionAndStore("set","set1","union");
Set union1 = redisTemplate.opsForSet().members("union");
System.out.println("存入union的set和set1的并集:{}"+union1);
//用法11:获取test中元素的个数
Long size = redisTemplate.opsForSet().size("union");
System.out.println("集合 union 的个数为:{}"+size);
//用法12:从test中随机弹出一个数,且删除元数据
redisTemplate.opsForSet().pop("set",1);
Set set5 = redisTemplate.opsForSet().members("set");
System.out.println("移除后的set数据:{}"+set5);
//用法13:将test中的a值移入到test1中
redisTemplate.opsForSet().move("set","1","set1");
Set set4 = redisTemplate.opsForSet().members("set");
Set set12 = redisTemplate.opsForSet().members("set1");
System.out.println("移动数据后的 set:{}"+set4);
System.out.println("移动数据后的 set1:{}"+set12);
}
/**
* 使用RedisTemplate操作 Zset
*
* 使用场景:
* Zset的特点是有序不可重复,可以用于 排行榜、点击率、时间轴等操作
*/
@Test
public void ZsetTempalate(){
//在zset集合中添加 java,并将分值设置为 1
redisTemplate.opsForZSet().add("zset", "java",1);
redisTemplate.opsForZSet().add("zset", "C",2);
redisTemplate.opsForZSet().add("zset", "C++",3);
redisTemplate.opsForZSet().add("zset", "C#",40);
redisTemplate.opsForZSet().add("zset", "python", 50);
redisTemplate.opsForZSet().add("zset", "PHP", 60);
//取出Java 的分值
Double score = redisTemplate.opsForZSet().score("zset", "java");
System.out.println("java 的score 为:{}"+score);
//用法3:获取zset中成员的数量
long count=redisTemplate.opsForZSet().zCard("zset");
System.out.println("zset的成员数量:{}"+count);
//用法4:删除zset集合中的java
String []arr={"java"};
Long zset = redisTemplate.opsForZSet().remove("zset",arr);
//根据score值取出zset的值,-1表示全部取出
Set zset1 = redisTemplate.opsForZSet().range("zset", 0, -1);
System.out.println("删除Java之后的集合:{}"+zset1);
//按用法5:照范围删除,-1表示最后
redisTemplate.opsForZSet().removeRange("zset",0,1);
Set zset2 = redisTemplate.opsForZSet().range("zset", 0, -1);
System.out.println("删除0-1之后的集合:{}"+zset2);
//用法6:删除集合zset中score在20-30的所有数据
redisTemplate.opsForZSet().removeRangeByScore("zset",40,40);
Set zset3 = redisTemplate.opsForZSet().range("zset", 0, -1);
System.out.println("按照分值删除后的数据为:{}"+zset3);
//用法7:按照score从大到小返回数据
Set<Object> sets=redisTemplate.opsForZSet().reverseRange("zset",0,-1);
System.out.println("根据score值由大到小排序为:{}"+sets);
//用法8:返回集合zset中20-30的数据,且按照从低到高排序(MAX值不能为-1)
Set zset4 = redisTemplate.opsForZSet().rangeByScore("zset", 0, 60);
System.out.println("根据score值由小到大排序:{}"+zset4);
//用法9:为集合zset中java的score值增加5
redisTemplate.opsForZSet().incrementScore("zset","python",5);
Double score1 = redisTemplate.opsForZSet().score("zset", "python");
System.out.println("增加分值后的python分值为:{}"+score1);
//用法10:获取分数在20-30之间的成员个数
Long zset5 = redisTemplate.opsForZSet().count("zset", 20, 55);
System.out.println("获取分数为20-50之间的数据个数为:{}"+zset5);
//用法11:按照从小到大规则,返回java在集合zset中的排名
Long rank = redisTemplate.opsForZSet().rank("zset", "PHP");
System.out.println("由大到小 PHP在排名中的位数为:{}"+rank);
//用法12:按照从大到小规则,返回java在集合zset中的排名
Long aLong = redisTemplate.opsForZSet().reverseRank("zset", "PHP");
System.out.println("由小到大,PHP在排名中的位数为:{}"+aLong);
}
/**
*使用redisTemplate操作 Hash
*
* 使用场景:
* Hash类型可以将多个键值对存入一个Redis的健当中,散列非常适用于将一些相关的数据存储在一起,所以可以用于将一个对象存储在Redis当中
*
*/
@Test
public void HashTemplate() throws InterruptedException {
//用法1:put->添加一条hash参数,key为id,value为1001
redisTemplate.opsForHash().put("stu1","id","1001");
Map<String,Object> allKeyAndValue=redisTemplate.opsForHash().entries("stu1");
System.out.println("获取到的学生数据为:{}"+allKeyAndValue);
Map<String,Object> map=new HashMap<String,Object>();
map.put("name","晓春");
map.put("sex","男");
map.put("age",10);
//用法2:putAll->:同时向stu1中添加多条数据
redisTemplate.opsForHash().putAll("stu1",map);
Map stu1 = redisTemplate.opsForHash().entries("stu1");
System.out.println("添加多条数据后的学生数据为:{}"+stu1);
//用法3:get->获取stu1中key为name的值
String name = (String) redisTemplate.opsForHash().get("stu1", "name");
System.out.println("获取stu1的学生姓名为:{}"+name);
//用法4:increment->为stu1中sex的值增加2
redisTemplate.opsForHash().increment("stu1","age", 1);
Integer age = (Integer)redisTemplate.opsForHash().get("stu1", "age");
System.out.println("修改后的学生年龄为:{}"+age);
//用法5:delete->删除stu1中的name
redisTemplate.opsForHash().delete("stu1","sex");
Map stu11 = redisTemplate.opsForHash().entries("stu1");
System.out.println("删除sex之后的数据:{}"+stu11);
//用法7:multiGet->获取stu1中,id和name的值
List<String> list=redisTemplate.opsForHash().multiGet("stu1",Arrays.asList("id","name"));
System.out.println("获取到的id和name:{}"+list);
//用法8:putIfAbsent->id存在就返回false且不执行任何动作,id不存在就添加id和value
boolean flag=redisTemplate.opsForHash().putIfAbsent("stu1","sex","女");
Map stu12 = redisTemplate.opsForHash().entries("stu1");
System.out.println("不存在添加之后的数据为:{}"+stu12);
//用法10:randomEntry->随机获取stu1中的一个键值对,如name=晓春或sex=1
String key1=redisTemplate.opsForHash().randomEntry("stu1").toString();
System.out.println("随机弹出的Key是:{}"+key1);
Map stu13 = redisTemplate.opsForHash().entries("stu1");
System.out.println("随机弹出之后的数据:{}"+stu13);
//用法11:size->查询stu1中键值对的数量
Long stu14 = redisTemplate.opsForHash().size("stu1");
System.out.println("获取stu1中的键值对数量:{}"+stu14);
//用法9:expire->设置stu1的有效为30秒
redisTemplate.expire("stu1",3,TimeUnit.SECONDS);
Long stu16 = redisTemplate.getExpire("stu1", TimeUnit.SECONDS);
System.out.println("时间有效期为:{}"+stu16);
Thread.sleep(4000);
Map stu15 = redisTemplate.opsForHash().entries("stu1");
System.out.println("失效后的数据为:{}"+stu15);
}
/**
* redisTemplate通用操作
*/
@Test
public void redisTemplateTest(){
//用法1:获取指定的keyz值,如*,??list,keys*
redisTemplate.keys("");
//用法2:删除指定的key
redisTemplate.delete("");
//用法3:给key重命名
redisTemplate.rename("","");
//用法4:设置key的有效期
redisTemplate.expire("username",3000,TimeUnit.SECONDS);
//用法5:获取key的有效期
redisTemplate.getExpire("username",TimeUnit.SECONDS);
//用法6:清楚key的有效期
redisTemplate.persist("username");
//用法7:判断key的类型
redisTemplate.type("");
}
}