一、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