Spring整合Redis
1.pom.xml
<!--redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.application.properties
#RedisProperties
##{选库,redis自带16个库,选择第12个,也就是11}
spring.redis.database=11
##{配置ip地址}
spring.redis.host=localhost
##{配置端口号}
spring.redis.port=6379
3.RedisConfig
写一个配置类,构造redisTemplate组件,这是由spring提供给我们的.
springboot也对redis做了一个自动的配置,自己配好了RedisTemplate的配置。因为redis是<key,value>的结构的,它把key做成了Object类型,更通用。
但是用的时候我们几乎都是<Spring,value>,使用Object调用起来不方便。所以使用配置类对RedisTemplate做一个重新的配置。
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory){
//1.redisTemplate要想具备访问数据库的能力,就得创建连接;连接是由连接工厂创建的,所以把连接工厂注入进来
RedisTemplate<String, Object> template = new RedisTemplate<>(); //1.1 在参数上声明一个连接工厂,上面括号;然后实例化这个bean
template.setConnectionFactory(factory); //1.2 把工厂设置给template,现在就有了访问数据库的能力
//2.我们配这个template主要配的是序列化方式
//因为写的程序是java程序,得到的数据是java类型的数据,最终要把数据存到redis数据库里,所以要指定一种序列化的方式,或者说数据转换的方式
//2.1 设置key的序列化方式
template.setKeySerializer(RedisSerializer.string());
//2.2 设置value的序列化方式
template.setValueSerializer(RedisSerializer.json());
/*有一个value比较特殊,就是 hash。这个value本身就是一个hash,hash又说明key、value*/
//2.3 设置 hash的key的序列化方式
template.setHashKeySerializer(RedisSerializer.string());
//2.4 设置 hash的value的序列化方式
template.setHashKeySerializer(RedisSerializer.json());
template.afterPropertiesSet();
return template;
}
}
4. RedisTests测试
查看第12个库,里面任何数据都没有。
RedisTests
@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = CommunityApplication.class)
public class RedisTests {
@Autowired
private RedisTemplate redisTemplate;
/*访问以字符串为值(value)的数据*/
@Test
public void testStrings(){
String redisKey = "test:count";
redisTemplate.opsForValue().set(redisKey,2); //设置value为2
System.out.println(redisTemplate.opsForValue().get(redisKey)); //取数据
System.out.println(redisTemplate.opsForValue().increment(redisKey)); //数据+1
System.out.println(redisTemplate.opsForValue().decrement(redisKey)); //数据-1
}
/*访问以 hash为 value的数据*/
@Test
public void testHashes(){
String redisKey = "test:user";
redisTemplate.opsForHash().put(redisKey,"id",17);
redisTemplate.opsForHash().put(redisKey,"name","YTY");
System.out.println(redisTemplate.opsForHash().get(redisKey, "id"));
System.out.println(redisTemplate.opsForHash().get(redisKey, "name"));
}
/*访问以 list列表为 value的数据*/
@Test
public void testLists(){
String redisKey = "test:ids";
redisTemplate.opsForList().leftPush(redisKey, 101); //从左边进
redisTemplate.opsForList().leftPush(redisKey, 201);
redisTemplate.opsForList().leftPush(redisKey, 401);
redisTemplate.opsForList().rightPush(redisKey, 301); //从右边进
//顺序应为 401 201 101 301
System.out.println(redisTemplate.opsForList().size(redisKey)); //获取当前列表中一共有多少个数据 —— 4
System.out.println(redisTemplate.opsForList().index(redisKey,2)); //获取当前列表中下标为2的数据 —— 101
System.out.println(redisTemplate.opsForList().range(redisKey, 0, 2)); //获取范围 0到2的数据,所以301不会在里面 —— 【401,201,101】
System.out.println(redisTemplate.opsForList().leftPop(redisKey)); //从左边弹出一条数据 —— 弹奏401
System.out.println(redisTemplate.opsForList().rightPop(redisKey)); //从右边弹出一条数据 —— 弹走301
System.out.println(redisTemplate.opsForList().size(redisKey)); //获取当前列表中一共有多少个数据 —— 2
}
/*访问以 set集合为 value的数据*/
@Test
public void testSets(){
String redisKey = "test:teacher";
redisTemplate.opsForSet().add(redisKey,"张飞","刘备","关羽","貂蝉","西施");
System.out.println(redisTemplate.opsForSet().size(redisKey)); //查看集合中数据个数
System.out.println(redisTemplate.opsForSet().pop(redisKey)); //随机弹出一个数:可用于抽奖啥的
System.out.println(redisTemplate.opsForSet().members(redisKey)); //查看集合中具体数据是什么
}
/*访问以 zset有序集合为 value的数据*/
@Test
public void testSortedSets(){
String redisKey = "test:students";
redisTemplate.opsForZSet().add(redisKey,"孙悟空",10);
redisTemplate.opsForZSet().add(redisKey,"唐僧",2);
redisTemplate.opsForZSet().add(redisKey,"猪八戒",7);
redisTemplate.opsForZSet().add(redisKey,"沙悟净",5);
redisTemplate.opsForZSet().add(redisKey,"白龙马",4);
System.out.println(redisTemplate.opsForZSet().zCard(redisKey)); //统计集合一个多少个数据 —— 5
System.out.println(redisTemplate.opsForZSet().score(redisKey, "白龙马")); //查看某一个value值的分数 —— 4
System.out.println(redisTemplate.opsForZSet().rank(redisKey, "唐僧")); //查看某一个value的排名(从小到大)——0
System.out.println(redisTemplate.opsForZSet().reverseRank(redisKey, "孙悟空")); //查看某一个value的排名(从大到小)——0
System.out.println(redisTemplate.opsForZSet().range(redisKey, 0, 3)); //取0-3范围内的数据(从小到大)
System.out.println(redisTemplate.opsForZSet().reverseRange(redisKey, 0, 3));//取0-3范围内的数据(从大到小)
}
}
4.1 ❤事务
事务:redis也是一个数据库,它也支持事务的。但是它满足事务的机制不满足ACID,因为毕竟是NoSql数据库。
redis的事务管理是比较简单的,机制
是:启用事务以后,当我再去执行一个redis命令的时候,它并不会立刻执行这个命令,而是把这个命令放到队列里先存着。
然后你再执行一个命令,它再放到队列里,直到你操作完了,你提交事务的时候,它会把队列中的命令一并发给redis服务器一起执行,是这样一个机制。
所以,这里就隐含了一个问题,使用的时候一定要注意,因为事务之内的命令不会立刻执行,提交时统一批量的执行。
所以说,如果在事务过程中做了一个查询,这个查询不会立刻返回结果;也就是不要在事务中间去做查询
,要么提前查,要么事务提交以后再查。这一点和关系型数据库不一样。
在开发的时候,声明式事务更简单,只做一些配置+@Transaction即可,但是它只能精确到一个方法,整个方法范围内逻辑都是事务范围,这个方法之内就没法去查询了。
所以一般用编程式事务,把事务范围缩小。比如中间只有两步需要管理事务,我就把这两步写到相关的代码里,其他地方该咋就咋。
/*演示编程式事务*/
@Test
public void testTransactional(){
Object obj = redisTemplate.execute(new SessionCallback() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
String redisKey = "test:tx";
operations.multi();//启用事务 下面可做一些相关操作
operations.opsForSet().add(redisKey,"zhangsan"); //添加数据
operations.opsForSet().add(redisKey,"lisi");
operations.opsForSet().add(redisKey,"wangwu");
//查询 - 事务内是没效果的,这里测试看一下
System.out.println(operations.opsForSet().members(redisKey));
return operations.exec(); //提交事务
}
});
System.out.println(obj);
}
}
5.结果:
1
2
3
4
弹出了西施,查看具体数据就没了西施。
5
6
确实被删了
十秒后,再次查看 test:users 还在不在:
不在了。