Jedis
我们要使用Java来操作Redis
什么是Jedis是Redis官方推荐的java连接开发工具?使用Java操作Redis中间件!如果你要使用java操作redis,那么一定要对jedis十分的熟悉!
测试步骤:
- 导入对应的依赖
<!--导入jedis的包-->
<dependencies>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.2.0</version>
</dependency>
<!--fastjson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
</dependencies>
- 编码测试:
- 连接数据库
- 操作命令
- 断开连接!
public class TestRedis {
public static void main(String[] args) {
// 1、new Jedis 对象即可
Jedis jedis = new Jedis("127.0.0.1",6379);
//jedis所有命令就是我们之前学习的所有指令!
System.out.printf(jedis.ping());//输出PONG表示连接成功
}
}
常用的API
String
List
Set
Hash
Zset
package com.geng;
import redis.clients.jedis.Jedis;
import java.util.concurrent.TimeUnit;
public class TestRedis {
public static void main(String[] args) {
// 1、new Jedis 对象即可
Jedis jedis = new Jedis("127.0.0.1",6379);
//jedis所有命令就是我们之前学习的所有指令!
System.out.printf(jedis.ping());//输出PONG表示连接成功
jedis.flushDB();//清空当前库的所有数据
System.out.println("========增加数据==========");
System.out.println(jedis.set("key1","value1"));
System.out.println(jedis.set("key2","value2"));
System.out.println(jedis.set("key3","value3"));
System.out.println("删除键key2"+jedis.del("key2"));
System.out.println("获取键key2"+jedis.get("key2"));
System.out.println("修改key1"+jedis.set("key1","value1Changed"));
System.out.println("获取key1的值:"+jedis.get("key1"));
System.out.println("在key3后面加入值:"+jedis.append("key3","End"));
System.out.println("key3的值:"+jedis.get("key3"));
System.out.println("增加多个键值对:"+jedis.mset("key01","value01","key02","value02","key03","value03"));
System.out.println("获取多个键值对:"+jedis.mget("key01","key02","key03"));
System.out.println("删除多个键值对:"+jedis.del("key01","key02"));
System.out.println("获取多个键值对:"+jedis.mget("key01","key02","key03"));
jedis.flushDB();
System.out.println("============新增键值对防止覆盖原先值=============");
System.out.println(jedis.setnx("key1","value1"));
System.out.println(jedis.setnx("key2","value2"));
System.out.println(jedis.setnx("key2","value2-new"));
System.out.println(jedis.get("key1"));
System.out.println(jedis.get("key2"));//获取的是value2
System.out.println("============新增键值对并设置有效时间=============");
System.out.println(jedis.setex("key3",2,"value3"));
System.out.println(jedis.get("key3"));
//睡眠三秒后key3不再能获取到
try{
TimeUnit.SECONDS.sleep(3);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(jedis.get("key3"));
System.out.println("========获取原值,更新为新值=========");
System.out.println(jedis.getSet("key2","key2GetSet"));
System.out.println(jedis.get("key2"));
System.out.println("获取key2的值的字符串:" + jedis.getrange("key2",2,4));
}
}
控制台输出:
PONG========增加数据==========
OK
OK
OK
删除键key21
获取键key2null
修改key1OK
获取key1的值:value1Changed
在key3后面加入值:9
key3的值:value3End
增加多个键值对:OK
获取多个键值对:[value01, value02, value03]
删除多个键值对:2
获取多个键值对:[null, null, value03]
============新增键值对防止覆盖原先值=============
1
1
0
value1
value2
============新增键值对并设置有效时间=============
OK
value3
null
========获取原值,更新为新值=========
value2
key2GetSet
获取key2的值的字符串:y2G
Process finished with exit code 0
所有的api命令,就是我们对应的上面学习的指令,一个都没有变化!
通过Jedis再次理解事务
package com.geng;
import com.alibaba.fastjson.JSONObject;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
public class TestTX {
public static void main(String[] args) {
Jedis jedis = new Jedis("127.0.0.1", 6379);
JSONObject jsonObject = new JSONObject();
jsonObject.put("hello","world");
jsonObject.put("name","kuangshen");
//开启事务
Transaction multi = jedis.multi();
String result = jsonObject.toJSONString();
try{
multi.set("user1",result);
multi.set("user2",result);
multi.exec();//执行事务
}catch (Exception e){
multi.discard();//放弃事务
e.printStackTrace();
}finally {
System.out.println(jedis.get("user1"));
System.out.println(jedis.get("user2"));
jedis.close();//关闭连接
}
}
}
控制台打印如下:
{"name":"kuangshen","hello":"world"}
{"name":"kuangshen","hello":"world"}
Process finished with exit code 0
现在我们在其中添加异常:
package com.geng;
import com.alibaba.fastjson.JSONObject;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
public class TestTX {
public static void main(String[] args) {
Jedis jedis = new Jedis("127.0.0.1", 6379);
jedis.flushDB();
JSONObject jsonObject = new JSONObject();
jsonObject.put("hello","world");
jsonObject.put("name","kuangshen");
//开启事务
Transaction multi = jedis.multi();
String result = jsonObject.toJSONString();
try{
multi.set("user1",result);
multi.set("user2",result);
int i = 1/0;
multi.exec();//执行事务
}catch (Exception e){
multi.discard();//放弃事务
e.printStackTrace();
}finally {
System.out.println(jedis.get("user1"));
System.out.println(jedis.get("user2"));
jedis.close();//关闭连接
}
}
}
控制台打印如下:
java.lang.ArithmeticException: / by zero
at com.geng.TestTX.main(TestTX.java:20)
null
null
Process finished with exit code 0
SpringBoot整合Redis
SpringBoot操作数据:spring-data jpa jdbc mongodb redis!
SpringData 也是和SpringBoot齐名的项目!
说明:在SpringBoot2.x之后,原来使用的jedis被替换为了lettuce
jedis:采用的直连,多个线程操作的话,是不安全的,如果想要避免不安全的,使用jedis pool连接池!BIO
lettuce:采用netty,实例可以在多个线程中进行共享,不存在线程不安全的情况!可以减少线程数据了,更像NIO模式
源码分析:
- 找到 spring.factories:spring-boot-autoconfigure-2.3.4.RELEASE.jar → META-INF → spring.factories
- 在 spring.factories 中搜索 redis
- 点进 RedisAutoConfiguration
/*
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.data.redis;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
/**
* {@link EnableAutoConfiguration Auto-configuration} for Spring Data's Redis support.
*
* @author Dave Syer
* @author Andy Wilkinson
* @author Christian Dupuis
* @author Christoph Strobl
* @author Phillip Webb
* @author Eddú Meléndez
* @author Stephane Nicoll
* @author Marco Aust
* @author Mark Paluch
* @since 1.0.0
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")//我们可以自己定义一个redisTemplate来替换这个默认的!
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
//默认的 RedisTemplate 没有过多的设置,redis对象都是需要序列化!
//两个泛型都是Object,Object的类型,我们后续使用需要强制转换<String,Object>
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean //由于String是redis中最常使用的类型,所以说单独提出来了一个bean!
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
SpringBoot所有的配置类,都有一个自动配置类,自动配置类都会绑定一个properties配置文件
整合测试一下
1、导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2、配置连接
#配置redis
spring.redis.host=127.0.0.1
spring.redis.port=6379
3、测试!
package com.itgeng.springboot_redis;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisTemplate;
@SpringBootTest
class SpringbootRedisApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
void contextLoads() {
//在企业开发中,我们80%的情况下,都不会使用这个原生的方式去编写代码!
//RedisUtils;
//redisTemplate 操作不同的数据类型
//opsForValue 操作字符串 类似String
//opsForList 操作List 类似List
//opsForSet
//opsForHash
//opsForZSet
//opsForGeo
//opsForHyperLogLog
//除了基本的操作,我们常用的方法都可以直接通过redisTemplate操作,比如事务,和基本的CRUD
//获取
// RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
// connection.flushDb();
// connection.flushAll();
redisTemplate.opsForValue().set("mykey","我的值");
System.out.println(redisTemplate.opsForValue().get("mykey"));
}
}
我的值
在redis客户端输入keys * 得到以下结果:
redis 127.0.0.1:6379> keys *
1) "\xac\xed\x00\x05t\x00\x05mykey"
为什么要序列化?
向数据库中插入了一个中文字符串,虽然在 Java 端可以看到返回了中文,但是在 Redis 中查看是一串乱码。
真实开发redis都是我们自定义的redisTemplate
pojo类:
package com.itgeng.springboot_redis.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.stereotype.Component;
import java.io.Serializable;
@Component
@AllArgsConstructor
@NoArgsConstructor
@Data
//在企业中,我们所有的pojo都会序列化!SpringBoot
public class User implements Serializable {
private String name;
private int age;
}
redisTemplate模板:
@Configuration
public class RedisConfig {
/**
* 编写自定义的 redisTemplate
* 这是一个比较固定的模板
*/
@Bean
@SuppressWarnings("all")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
// 为了开发方便,直接使用<String, Object>
RedisTemplate<String, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
// Json 配置序列化
// 使用 jackson 解析任意的对象
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
// 使用 objectMapper 进行转义
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
// String 的序列化
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key 采用 String 的序列化方式
template.setKeySerializer(stringRedisSerializer);
// Hash 的 key 采用 String 的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value 采用 jackson 的序列化方式
template.setValueSerializer(jackson2JsonRedisSerializer);
// Hash 的 value 采用 jackson 的序列化方式
template.setHashValueSerializer(jackson2JsonRedisSerializer);
// 把所有的配置 set 进 template
template.afterPropertiesSet();
return template;
}
}
在我们真实开发中,或者你们公司,一般都可以看到一个公司自己封装的RedisUtil工具类。
这样调用
@Autowire
private RedisUtil redisUtil;
所有的redis操作,其实对于java开发人员来说,十分的简单,更重要的是要去理解redis的思想和每一种数据结构的用处和作用场景!