使用Java操作Redis的两种方式Jedis、RedisTemplate

举报
扎哇太枣糕 发表于 2022/09/23 08:57:54 2022/09/23
【摘要】   各位小伙伴们大家好,欢迎来到这个小扎扎的Redis 6专栏,在这个系列专栏中我对B站黑马的Redis教程进行一个总结,鉴于 看到就是学到、学到就是赚到 精神,这波依然是血赚 ┗|`O′|┛   我们知道,redis是一个很强大的NoSQL数据库,从上面那一张图就可以看出来它分别拥有支持多种语言的客户端,Java语言就是其中之一。上面的截图来源于官网:https://redis.io/...

  各位小伙伴们大家好,欢迎来到这个小扎扎的Redis 6专栏,在这个系列专栏中我对B站黑马的Redis教程进行一个总结,鉴于 看到就是学到、学到就是赚到 精神,这波依然是血赚 ┗|`O′|┛

在这里插入图片描述  我们知道,redis是一个很强大的NoSQL数据库,从上面那一张图就可以看出来它分别拥有支持多种语言的客户端,Java语言就是其中之一。上面的截图来源于官网:https://redis.io/docs/clients/在这里插入图片描述  点开Java可以看到,官方推荐我们使用前三种Redisson、Jedis、Lettuce客户端,那么他们分别有何优缺点,我们又该如何选择呢?

  • Redisson是一个基于Redis实现的分布式、可伸缩的Java数据结构集合。包含了诸如Map、Queue、Lock、 Semaphore、AtomicLong等强大功能
  • Jedis以Redis命令作为方法名称,学习成本低,简单实用。但是Jedis实例是线程不安全的,多线程环境下必须需要使用连接池来连接
  • Lettuce是基于Netty实现的,支持同步、异步和响应式编程方式,并且是线程安全的。支持Redis的哨兵模式、集群模式和管道模式

  基于目前的使用状况,我们先学习Jedis的原生API,然后再学习Spring整合Spring Data Redis框架,该框架底层很好的兼容了Jedis和Lettuce

🍖 Jedis的使用

🥩 Jedis快速入门

  官网地址: https://github.com/redis/jedis
第一步: 导依赖

<!--jedis依赖-->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.7.0</version>
</dependency>

<!--单元测试依赖-->
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.7.0</version>
</dependency>

第二步: 与redis建立连接,在Java中的体现就是实例化一个Jedis对象,构造器的参数需要虚拟机的ip地址,可以使用如下命令查看在这里插入图片描述

/**
 * 与Redis建立连接 @BeforeEach表示每个方法执行之前都需要执行该方法
 * @Param void
 * @return
 */
@BeforeEach
void setUp() {
    // 建立连接
    jedis = new Jedis("你的虚拟机ip", 6379);
    // 设置redis的密码
    jedis.auth("你的redis密码");
    // 选择使用的库 默认是0号库
    jedis.select(0);
}

第三步: 测试各种添加查询操作,API的名和redis的命令一致,其他操作参考redis的命令

/** 
 * K-V的测试
 * @Param void 
 * @return 
 */
@Test
void testHash() {
    Map<String, String> map = new HashMap<String, String>();
    map.put("id", "1");
    map.put("username", "zhagnsan");
    map.put("age", "20");
    // 存入数据
    jedis.hset("user", map);

    // 获取命令
    Set<String> userKeys = jedis.hkeys("user");
    List<String> userValues = jedis.hvals("user");

    // 输出获取的结果
    System.out.println(userKeys);
    System.out.println("============================");
    System.out.println(userValues);
}

第四步: 断开连接

/**
 * 关闭连接  @AfterEach表示每个方法执行之后都需要执行该方法
 * @Param void
 * @return
 */
@AfterEach
void tearDown() {
    if (jedis != null) {
        jedis.close();
    }
}
redis.clients.jedis.exceptions.JedisConnectionException: Failed to create socket.

  如果报上面的错误连接不上的话,可以参考这篇博客https://blog.csdn.net/qq_44624536/article/details/120213607,如果还是无法解决的话,很有可能就是虚拟机的防火墙没有关闭,可以使用如下命令查看并关闭一下(我的就是这个错误原因)

# 查看防火墙状态的命令
systemctl status firewalld
# 关闭防火墙的命令
systemctl stop firewalld

在这里插入图片描述运行结果在这里插入图片描述

🥩 Jedis连接池

  上面说过Jedis本身是线程不安全的,如果在多线程并发操作下极有可能出现线程安全问题,因此在并发的环境下,一定要为每一个线程创建一个独立的线程对象。但是频繁的创建销毁连接会导致性能损耗,因此需要使用到Jedis连接池来代替Jedis的直接连接方式。
第一步: 使用工具类创建并配置连接池对象,并使用静态方法返回连接

public class JedisConnectionFactory {
    private static final JedisPool JEDIS_POOL;

    static {
        // 配置连接池
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        // 设置最大连接数、最大空闲连接、最小空闲连接、设置无连接的等待时间(毫秒为单位)
        jedisPoolConfig.setMaxTotal(8);
        jedisPoolConfig.setMaxIdle(8);
        jedisPoolConfig.setMinIdle(0);
        jedisPoolConfig.setMaxWaitMillis(1000);
        // 创建连接池对象
        JEDIS_POOL = new JedisPool(jedisPoolConfig, "你的虚拟机ip", 6379, 1000, "你的redis密码");
    }

    public static Jedis getJedis() {
        return JEDIS_POOL.getResource();
    }
}

第二步: 调用静态方法建立连接即可,剩下的操作都一样

@BeforeEach
void setUp() {
    // 建立连接
    jedis = JedisConnectionFactory.getJedis();
    // 选择使用的库 默认是0号库
    jedis.select(0);
}

🍖 SpringBoot整合RedisTemplate

🥩 自定义配置RedisTemplate

第一步: 导入SpringBoot整合RedisTemplate的依赖,连接池的依赖,SpringBoot项目不用指定技术版本,继承使用父工程中已经配置好的版本

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

第二步: 配置信息

spring:
  redis:
    host: 你的虚拟机ip
    port: 6379
    password: 你的redis密码
    lettuce:
      pool:
        max-active: 8
        max-idle: 8
        min-idle: 0
        max-wait: 1000

第三步: 直接注入redisTemplate对象,使用它内部的API

@Autowired
private RedisTemplate redisTemplate;

@Test
void contextLoads() {
    redisTemplate.opsForValue().set("name", "zhangsan");

    Object name = redisTemplate.opsForValue().get("name");
    System.out.println("name ==>" + name);
}

结果显示并没有问题,可以正常的存入读出在这里插入图片描述但是切换到redis中查看就有问题了,没有name这个key,但是有一个乱码的K-V生成,这是什么原因呢?是因为redisTemplate底层将这个K-V当做对象进行了默认的jdk序列化操作在这里插入图片描述jdk序列化差生的结果会有很多的问题,结果可读性很差,短字符串的话又会很占用内存空间,于是我们可以选择使用其他的序列化方式,使用配置类替代默认的序列化配置

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        // 创建RedisTemple对象
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        // 设置连接工厂
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        // 创建JSON序列化工具
        GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        // 设置K序列化
        redisTemplate.setKeySerializer(RedisSerializer.string());
        redisTemplate.setHashKeySerializer(RedisSerializer.string());
        // 设置V序列化
        redisTemplate.setValueSerializer(jsonRedisSerializer);
        redisTemplate.setHashValueSerializer(jsonRedisSerializer);
        // 返回RedisTemplate对象
        return redisTemplate;
    }
}

由于使用到了JSON所以需要导入相关依赖,如果导入mvc的依赖的话会默认导入JSON但是,我们的项目没有导MVC也没有JSON,所以需要导一下

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>

在这里插入图片描述
按理来说,value也可以是一个对象,redisTemplate会将其序列化为JSON对象进行存储

 @Test
void test01() {
    redisTemplate.opsForValue().set("user", new User("zhangsan", 20));

    Object name = redisTemplate.opsForValue().get("name");
    System.out.println("user ==> " + name);
}

在这里插入图片描述

🥩 StringRedisTemplate

  由于上面使用配置类自定义序列化的方式,导致序列化之后的字符串会带",而且Jason对象序列化之后会带有类的全限定名,数据量大了之后也会影响到存储的性能。于是出现了StringRedisTemplate,这种模板很好的解决了上述问题,但是需要我们手动的将对象进行序列化和反序列化

@SpringBootTest
public class StringRedistemplateTest {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Test
    void contextLoads() {
        stringRedisTemplate.opsForValue().set("name", "lisi");

        Object name = stringRedisTemplate.opsForValue().get("name");
        System.out.println("name ==>" + name);
    }

    // 创建ObjectMapper对象用于序列化和反序列化
    private static final ObjectMapper MAPPER = new ObjectMapper();

    @Test
    void test01() throws JsonProcessingException {
        // 创建user对象  并序列化对象
        User user = new User("zhangsan", 20);
        String userJson = MAPPER.writeValueAsString(user);

        // 写数据
        stringRedisTemplate.opsForValue().set("user", userJson);
        // 读数据并反序列化输出
        String res = stringRedisTemplate.opsForValue().get("user");
        User jsonUser = MAPPER.readValue(res, User.class);
        System.out.println("user ==> " + jsonUser);
    }
}

在这里插入图片描述

@Test
void testHash() {
    stringRedisTemplate.opsForHash().put("user01", "name", "lisi");
    stringRedisTemplate.opsForHash().put("user01", "age", "21");

    Map<Object, Object> user01 = stringRedisTemplate.opsForHash().entries("user01");
    System.out.println(user01);
}

在这里插入图片描述

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。