Java整合Redis
服务端配置
首先配置Linux端:开放服务器防火墙的redis的端口
[root@localhost bin]# firewall-cmd --get-active-zones
public
interfaces: ens33
[root@localhost bin]# firewall-cmd --zone=public --add-port 6379/tcp --permanent
success
[root@localhost bin]# firewall-cmd --reload
success
[root@localhost bin]# firewall-cmd --query-port=6379/tcp
yes
修改redis的配置文件:bind远程可以访问的地址(也就是服务器在外部网络可以访问到的地址)
1、Jedis
客户端使用
简单事务
通过jedis.multi()的返回值对象,进行事务的执行与提交等操作。
public static void main(String[] args) {
Jedis jedis = new Jedis("192.168.37.100",6379);
jedis.flushDB();
JSONObject jsonObject = new JSONObject();
jsonObject.put("name", "fall");
jsonObject.put("sex", "man");
String s = jsonObject.toJSONString(); // 准备数据并转换成String格式
Transaction multi = jedis.multi(); // 开启事务,得到返回值
try {
multi.set("role", s); // 通过开启事务的返回值,在事务中进行命令执行
int i = 1/0; // 如果这里触发了异常,就会进入catch块
multi.exec(); // 如果正常执行,就执行事务
} catch (Exception e) {
multi.discard(); // 触发异常就撤销事务
e.printStackTrace();
} finally {
System.out.println("get role:"+jedis.get("role")); // 最后get role,看看是否放入成功了
jedis.close(); // 关闭jedis连接
}
}
WATCH监控
监控一个元素达到防止数据出错的目的。
public static void testWatch(){
Jedis jedis = new Jedis("192.168.37.100",6379);
jedis.flushDB();
jedis.set("age", "18");
// 监控age元素
jedis.watch("age");
Transaction multi = jedis.multi();
try {
// 先休眠5秒,再进行自增操作
TimeUnit.SECONDS.sleep(10);
// 在休眠期间进行修改age,就会触发watch,导致事务的执行失败
multi.incr("age");
// 设置一个其他的值
multi.set("other", "value");
multi.exec();
} catch (Exception e) {
multi.discard();
e.printStackTrace();
} finally {
System.out.println("age = " + jedis.get("age"));
System.out.println("other = " + jedis.get("other"));
jedis.close();
}
}
测试可以发现休眠期间在其他客户端对age进行192.168.37.100:6379> INCRBY age 20
自增20操作,事务的执行全部都失败了,
jedis.get(“other”)返回值为null:
2、SpringBoot整合Redis
简介
在SpringBoot2.x后,SpringBoot不再使用jedis来操作Redis,而是替换成了lettuce。
Jedis:采用直连,多线程操作时是不安全的,如果想要线程安全,就需要使用Jedis Pool连接池,更像BIO模式。
Lettuce:采用netty,实例可以在多个线程中共享,线程安全,更像NIO模式。
基础配置
# 使用spring.redis作为前缀,配置redis的相关信息
spring.redis.host=192.168.37.100
spring.redis.port=6379
操作Redis
默认配置
使用默认的RedisTemplate时,只需要自动注入,在使用时,想要操作什么数据类型就是用redisTemplate.opsForXXX类型
即可。
@SpringBootTest
class SpringbootRedisApplicationTests {
@Autowired
RedisTemplate<Object,Object> redisTemplate;
@Test
public void testRedis() {
// opsForValue操作String
redisTemplate.opsForValue().set("name", "fall");
System.out.println(redisTemplate.opsForValue().get("name"));
// opsForList操作列表
redisTemplate.opsForList().leftPush("myKey", "value1");
redisTemplate.opsForList().leftPush("myKey", "value2");
redisTemplate.opsForList().leftPush("myKey", "value3");
System.out.println(redisTemplate.opsForList().leftPop("myKey"));
System.out.println(redisTemplate.opsForList().leftPop("myKey"));
System.out.println(redisTemplate.opsForList().leftPop("myKey"));
// 得到连接对象
RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
// 可以通过连接对象进行redis操作(如清空、关闭连接等)
connection.flushDb();
connection.close();
}
}
默认配置保存java对象:(转换成字符串保存,或让实体类实现序列化接口来进行保存)
/**
* 测试保存对象
* 通过转换成JSON格式
*/
@Test
public void saveByJSON() throws JsonProcessingException {
// 创建一个对象
Person person = new Person("zs",18);
// 将对象转换成JSON格式的字符串
String jsonPerson = new ObjectMapper().writeValueAsString(person);
// 存入Redis
redisTemplate.opsForValue().set("person", jsonPerson);
// 取出
System.out.println(redisTemplate.opsForValue().get("person"));
}
/**
* 通过让对象实现序列化接口,
* 直接存储对象
*/
@Test
public void saveObject() {
// 创建一个对象
Person person = new Person("zs",18);
// 直接存入Redis
redisTemplate.opsForValue().set("person", person);
// 取出
System.out.println(redisTemplate.opsForValue().get("person"));
}
可以发现的使是,当直接通过这种方式保存时,redis中的key其实是经过一层编码的,显示的key不是我们输入的key,想要使RedisTemplate符合我们的习惯,就可以采用自定义RedisTemplate的方式。
自定义RedisTemplate
可以看到SpringBoot自带的RedisTemplate的源码:
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@ConditionalOnMissingBean(name = "redisTemplate")
这句注解,表示只要我们向Spring容器中加入一个RedisTemplate对象,那么这个Bean就不会生效,所以我们可以通过创建一个新的Bean,来达到自定义RedisTemplate的目的。
那么自定义的RedisTemplate代码如下(一般可以当坐模板用,不需要修改):
/**
* 配置类,注入自定义的RedisTemplate
*/
@Configuration
public class RedisConfig {
// 自定义一个方法返回RedisTemplate
@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String,Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
// JSON序列化配置
Jackson2JsonRedisSerializer jsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jsonRedisSerializer.setObjectMapper(objectMapper);
// String的序列化
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key采用String序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value采用jackson序列化方式
template.setValueSerializer(jsonRedisSerializer);
// hash的value采用jackson序列化方式
template.setHashValueSerializer(jsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
将该类与其中的方法加入Spring IOC容器后,自动注入时就会采用这个自定义的Template,此时存入Redis的key变成了我们自己输入的key的样式。
同样的,为了更方便地操作Redis,网上也有许多Redis的工具类,可以免除我们用Template操作Redis时一些比较麻烦的步骤(如需要先调用opsForXXX方法)。