一、Springboot配置Redis
pom.xml文件需要的依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <!--<version>2.1.4.RELEASE</version>--> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency>
注意: 1. 是spring-boot-starter-data-reds;
2. 因为Springboot 2.0 中redis客户端使用了Lettue, 其依赖于commons, 所以加入以上(似乎Jedis依然可以使用.)
redis服务: 下载redis的Windows版本, 官网是Linux版本, Windows版由微软在github上维护, 可前往下载, 压缩包解压之后, 点击redis-server.exe开启本地redis服务, 端口号6379. 可使用redisclient客户端查看数据库
二、application.properties配置
## 是否启动日志SQL语句 spring.jpa.show-sql=true # Redis 数据库索引(默认为 0) spring.redis.database=0 spring.redis.host=localhost spring.redis.port=6379 # Redis 服务器连接密码(默认为空) spring.redis.password= # springboot 2.0 redis默认客户端已换成lettuce # 连接池最大连接数(使用负值表示没有限制) 默认 8 spring.redis.lettuce.pool.max-active=8 # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1 spring.redis.lettuce.pool.max-wait=-1 # 连接池中的最大空闲连接 默认 8 spring.redis.lettuce.pool.max-idle=8 # 连接池中的最小空闲连接 默认 0 spring.redis.lettuce.pool.min-idle=0 spring.redis.timeout=5000
三、自定义序列化(推荐使用StringRedisTemplate)
为redis客户端查看操作数据, redisTemplate需要进行序列化设置, 默认配置的jdk序列化会导致在客户端查看不了数据(仍可使用内在函数存取修改, 只是查看不了), 为避免这种情况发生, 使用StringRedisTemplate或自行配置序列化, 自行配置可参考如下代码:
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.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration public class MyRedisConfig { @Bean(name = "redisTemplate") public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){ RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); ////参照StringRedisTemplate内部实现指定序列化器 redisTemplate.setConnectionFactory(redisConnectionFactory); redisTemplate.setKeySerializer(keySerializer()); redisTemplate.setHashKeySerializer(keySerializer()); redisTemplate.setValueSerializer(valueSerializer()); redisTemplate.setHashValueSerializer(valueSerializer()); return redisTemplate; } private RedisSerializer<String> keySerializer(){ return new StringRedisSerializer(); } //使用Jackson序列化器 private RedisSerializer<Object> valueSerializer(){ return new GenericJackson2JsonRedisSerializer(); } }
简单测试代码(test)
@RunWith(SpringRunner.class) @SpringBootTest public class MyConfigRedisTemplateTest { @Autowired private RedisTemplate redisTemplate; //在MyRedisConfig文件中配置了redisTemplate的序列化之后, 客户端也能正确显示键值对了 @Test public void test(){ redisTemplate.opsForValue().set("wujinxing", "lige"); System.out.println(redisTemplate.opsForValue().get("wujinxing")); Map<String, Object> map = new HashMap<>(); for (int i=0; i<10; i++){ User user = new User(); user.setId(i); user.setName(String.format("测试%d", i)); user.setAge(i+10); map.put(String.valueOf(i),user); } redisTemplate.opsForHash().putAll("测试", map); BoundHashOperations hashOps = redisTemplate.boundHashOps("测试"); Map map1 = hashOps.entries(); System.out.println(map1); } static class User implements Serializable { private int id; private String name; private long age; 省略getter, setter, toString... } }
四、redis操作string, hash, set等
常见操作(均在controller上使用, 仅做测试, 实际项目应在service层使用redis):
@Controller @RequestMapping("/redis") public class RedisController { private static final Logger LOGGER = LoggerFactory.getLogger(RedisController.class); @Autowired private RedisTemplate redisTemplate = null; @Autowired private StringRedisTemplate stringRedisTemplate = null; @RequestMapping("/stringAndHash") @ResponseBody public Map<String, Object> testStringAndHash(){ redisTemplate.opsForValue().set("key1", "value1"); //注意这里使用了 JDK 的序列化器 ,所以 Redis 保存时不是整数, 不能运算 redisTemplate.opsForValue().set("int_key", "1"); stringRedisTemplate.opsForValue().set("int", "1"); //使用运算 stringRedisTemplate.opsForValue().increment("int", 1); Map<String, Object> hash = new HashMap<>(); hash.put("field1", "value1"); hash.put("field2", "value2"); stringRedisTemplate.opsForHash().putAll("hash2", hash); //将Hashmap存储到redis中 stringRedisTemplate.opsForHash().put("hash2", "field3", "value3"); //绑定散列操作的 key,这样可以连续对同一个散列数据类型进行操作 BoundHashOperations hashOps = stringRedisTemplate.boundHashOps("hash2"); hashOps.delete("field2", "field1"); //删除元素 hashOps.put("field4", "value4"); //添加元素 //LOGGER.info(hashOps.entries().toString()); Map<String, Object> map = new HashMap<>(); map.put("success", true); return map; } @RequestMapping("/list") @ResponseBody public Map<String, Object> testList(){ //链表从左到右的顺序为v10, v8, v6, v4, v2 stringRedisTemplate.opsForList().leftPushAll("list1", "v2","v4","v6","v8","v10"); //链表从左到右的顺序为v1, v3, v5, v7, v9 stringRedisTemplate.opsForList().rightPushAll("list2", "v1","v3","v5","v7","v9"); //绑定list2操作链表 BoundListOperations listOps = stringRedisTemplate.boundListOps("list2"); Object result1 = listOps.rightPop();//从右边弹出一个成员 LOGGER.info("list2的最右边元素为: "+result1.toString()); Object result2 = listOps.index(1); //获取定位元素, 下标从0开始 LOGGER.info("list2下标为1的元素为"+result2.toString()); listOps.leftPush("v0"); //从左边插入链表 Long size = listOps.size();//求链表长 LOGGER.info("list2的长度为: "+size); List element = listOps.range(0, size-2); //求链表区间成员 LOGGER.info("list2从0到size-2的元素依次为: "+element.toString()); Map<String, Object> map = new HashMap<>(); map.put("success", true); return map; } @RequestMapping("/set") @ResponseBody public Map<String, Object> testSet(){ //重复的元素不会被插入 stringRedisTemplate.opsForSet().add("set1", "v1","v1","v3","v5","v7","v9"); stringRedisTemplate.opsForSet().add("set2", "v2","v4","v6","v5","v10","v10"); //绑定sert1集合操作 BoundSetOperations setOps = stringRedisTemplate.boundSetOps("set1"); setOps.add("v11", "v13"); setOps.remove("v1", "v3"); Set set = setOps.members();//返回所有元素 LOGGER.info("集合中所有元素: "+set.toString()); Long size = setOps.size();//求成员数 LOGGER.info("集合长度: "+String.valueOf(size)); Set inner = setOps.intersect("set2"); //求交集 setOps.intersectAndStore("set2", "set1_set2");//求交集并用新的集合保存 LOGGER.info("集合的交集: "+inner.toString()); Set diff = setOps.diff("set2"); //求差集 setOps.diffAndStore("set2","set1-set2"); //求差集并用新的集合保存 LOGGER.info("集合的差集: "+diff.toString()); Set union = setOps.union("set2"); //求并集 setOps.unionAndStore("set2", "set1=set2"); //求并集并用新的集合保存 LOGGER.info("集合的并集: "+union.toString()); Map<String, Object> map = new HashMap<>(); map.put("success", true); return map; } /** * redis操作有序集合 * @return */ @RequestMapping("/zset") @ResponseBody public Map<String, Object> testZSet(){ Set<ZSetOperations.TypedTuple<String>> typedTupleSet = new HashSet<>(); for(int i=1; i<=9; i++){ //分数 double score = i*0.1; //创建一个TypedTuple对象, 存入值和分数 ZSetOperations.TypedTuple typedTuple = new DefaultTypedTuple<String>("value" + i, score); typedTupleSet.add(typedTuple); } LOGGER.info("新建的set: "+typedTupleSet.toString()); //往有序集合插入元素 stringRedisTemplate.opsForZSet().add("zset1", typedTupleSet); //绑定zset1有序集合操作 BoundZSetOperations<String, String> zSetOps = stringRedisTemplate.boundZSetOps("zset1"); zSetOps.add("value10", 0.26); Set<String> setRange = zSetOps.range(1,6); LOGGER.info("下标下1-6的set: " + setRange.toString()); //按分数排序获取有序集合 Set<String> setScore = zSetOps.rangeByScore(0.2, 0.6); LOGGER.info("按分数排序获取有序集合: "+ setScore.toString()); //定义值范围 RedisZSetCommands.Range range = new RedisZSetCommands.Range(); range.gt("value3"); //大于value3 //range.gte("value3"); //大于等于value3 //range.lt("value8"); //小于value8 range.lte("value8"); //小于等于value8 //按值排序, 注意这个排序是按字符串排序 Set<String> setLex = zSetOps.rangeByLex(range); LOGGER.info("按值排序: "+setLex.toString()); zSetOps.remove("value9", "value2"); //删除元素 Double score = zSetOps.score("value8"); //求分数 LOGGER.info("求value8的分数: "+score); //在下标区间 按分数排序, 同时返回value和score Set<ZSetOperations.TypedTuple<String>> rangeSet = zSetOps.rangeWithScores(1,6); LOGGER.info("在下标区间 按分数排序, 同时返回value和score: "+rangeSet.toString()); //在下标区间 按分数排序, 同时返回value和score Set<ZSetOperations.TypedTuple<String>> scoreSet = zSetOps.rangeByScoreWithScores(1,6); LOGGER.info("在下标区间 按分数排序, 同时返回value和score: "+scoreSet.toString()); //按从大到小排序 Set<String> reverseSet = zSetOps.reverseRange(2, 8); LOGGER.info("按从大到小排序: "+reverseSet.toString()); Map<String, Object> map = new HashMap<>(); map.put("success", true); return map; } @RequestMapping("/multi") @ResponseBody public Map<String, Object> testMulti(){ stringRedisTemplate.opsForValue().set("key1", "value1"); /*List list = (List) stringRedisTemplate.execute((RedisOperations operations)->{ operations.watch("key1"); operations.multi(); operations.opsForValue().set("key2", "value2"); //operations.opsForValue().increment("key1", 1); //获取的值将为null, 因为redis知识把命令放入队列 Object value2 = operations.opsForValue().get("key2"); System.out.println("命令在队列, 所以value2为null [ " + value2 + " ] "); operations.opsForValue().set("key3", "value3"); Object value3 = operations.opsForValue().get("key3"); System.out.println("命令在队列, 所以value3为null [ " + value3 + " ] "); //执行exce()命令,将先判断key1是否在监控后被修改过, 如果是则不执行事务, 否则就执行事务 return operations.exec(); }); System.out.println(list);*/ Map<String, Object> map = new HashMap<>(); map.put("success", true); return map; } }
五、Service层使用redis
service层上简单使用redis的例子:
@Service public class CityServiceImpl implements CityService { private static final Logger LOGGER = LoggerFactory.getLogger(CityServiceImpl.class); @Autowired private CityMapper cityMapper; @Autowired private RedisTemplate redisTemplate; /** * 获取城市逻辑: * 如果缓存存在,从缓存中获取城市信息 * 如果缓存不存在,从 DB 中获取城市信息,然后插入缓存 */ @Override public City findCityById(Long id){ //从缓存中获取城市信息 String key = "city_"+id; ValueOperations<String,City> operations = redisTemplate.opsForValue(); //缓存存在 boolean hasKey = redisTemplate.hasKey(key); if(hasKey){ City city = operations.get(key); LOGGER.info("CityServiceImpl.findCityById() : 从缓存中获取了城市 >> " + city.toString()); return city; } //从DB中获取城市 City city = cityMapper.findById(id); //插入缓存 operations.set(key,city,10,TimeUnit.SECONDS); //缓存的时间仅有十秒钟 LOGGER.info("CityServiceImpl.findCityById() : 城市插入缓存 >> " + city.toString()); LOGGER.info("刚才加入redis的数据是: "+operations.get(key)); return city; } @Override public Long saveCity(City city) { return cityMapper.saveCity(city); } /** * 更新城市逻辑: * 如果缓存存在,删除 * 如果缓存不存在,不操作 */ @Override public Long updateCity(City city) { Long ret = cityMapper.updateCity(city); //缓存存在,删除缓存 String key = "city_" + city.getId(); boolean hasKey = redisTemplate.hasKey(key); if (hasKey){ redisTemplate.delete(key); LOGGER.info("CityServiceImpl.updateCity() : 从缓存中删除城市 >> " + city.toString()); } return ret; } @Override public Long deleteCity(Long id) { Long ret = cityMapper.deleteCity(id); String key = "city_" + id; boolean hasKey = redisTemplate.hasKey(key); if(hasKey){ redisTemplate.delete(key); LOGGER.info("CityServiceImpl.deleteCity() : 从缓存中删除城市 ID >> " + id); } return ret; } }
以上代码参考于<深入实践Springboot2.x>, 网上的相关教程等.
来源:https://www.cnblogs.com/kingstar718/p/10941958.html