首页 > 编程笔记

Spring Boot集成Redis

在 NoSQL 数据库这个分类中,Redis 也是非常流行并且极具特色的一款产品。与适合处理海量数据易于扩展的 MongoDB 不同,Redis 在性能方面更为突出。

Redis 作为内存型的键值数据库,以高度优化的存储结构将数据保存至内存,这使得该产品的性能可以在各数据库之中排到第一梯队。也是因为性能如此突出,在大多应用开发中,Redis 往往会被当成缓存使用,用于缓存其他数据库中的热点数据,以提高整个系统的查询性能。

本文将使用 Spring Data Redis 来整合 Redis 数据库,同时也会为开发人员提供模板以及 Repository 这两种通用的数据访问形式。

准备工作

为了能够使用到 Spring Data Redis 项目所提供的功能,需要引入对应的 starter 依赖:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
另外,连接 Redis 有两种客户端解决方案:
  1. Lettuce 。
  2. Jedis。

Spring Data Redis 默认集成了 Lettuce 作为连接客户端。如果要另外使用 Jedis,需要另外引入对应的依赖。
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.3.0</version>
</dependency>
在引入依赖之后,需要声明一个配置项用于启用 Repository 以及模板。示例代码如下:
@Bean
public LettuceConnectionFactory redisConnectionFactory(){
    //使用Lettuce作为客户端需要声明该Bean
    RedisStandaloneConfiguration redisStandaloneConfiguration=new RedisStandaloneConfiguration(hostName,port);
    //如果有密码,则需要通过setPassword设置对应密码
    redisStandaloneConfiguration.setPassword(password);
    return new LettuceConnectionFactory(redisStandaloneConfiguration);
}

@Bean
public JedisConnectionFactory jedisConnectionFactory(){
    //使用Jedis作为客户端需要声明该Bean
    RedisStandaloneConfiguration redisStandaloneConfiguration=new RedisStandaloneConfiguration(hostName,port);
    redisStandaloneConfiguration.setPassword(password);
    return new JedisConnectionFactory(redisStmndAloneConfigurmtion);
}

@Bean
public RedisTemplate<?, ?> redisTemplate(){
    RedisTemplate<String, Object> template=new RedisTemplate<>();
    RedisSerializer<String> stringSerializer=new StringRedisSerializer();
    JdkSerializationRedisSerializer jdkSerializationRedisSerializer=new JdkSerializationRedisSerializer();
    template.setConnectionFactory(redisConnectionFactory());
    template.setKeySerializer(stringSerializer);
    template.setHashKeySerializer(stringSerializer);
    template.setValueSerializer(jdkSerializationRedisSerializer);
    template.setHashValueSerializer(jdkSerializationRedisSerializer);
    template.setEnableTransactionSupport(true);
    template.afterPropertiesSet();
    return template;
}
其中 LettuceConnectionFactory 与 JedisConnectionFactory 根据所选的客户端选择声明对应 ConnectionFactory 即可。

使用RedisRepository访问Redis

RedisRepository 延续了 Spring Data 中通用的 Repository 的设计风格,为操作 Redis 带来了简单易用的方式。

示例

实体示例代码:
@Accessors(chain = true)
@Getter
@Setter
@RedisHash(value = "Student", timeToLive = 10)
public class Student implements Serializable {

    public enum Gender{
        MALE, FEMALE
    }

    private String id;
    @Indexed
    private String name;
    private Gender gender;
    private int grade;
}

示例代码 StudentRepository.java:
@Repository
public interface StudentRepository extends CrudRepository<Student, String> {

    //自定义查询方式。使用标注了@Indexed的name属性进行查询
    Student findByName(String name);
}
调用示例代码如下:
@SpringBootTest
class RedisApplicationTests {

    @Autowired
    StudentRepository studentRepository;

    @Test
    void testSave() {
        Student student = new Student().setId("20200101007").setName("zbc").setGender(Student.Gender.MALE).setGrade(1);
        //根据Id新增或更新记录
        studentRepository.save(student);
    }

    @Test
    void testFindBy() {
        //使用主键查询
        assert studentRepository.findById("20200101007").isPresent();
        //根据自定义方法查询
        assert studentRepository.findByName("zbc") != null;
    }

    @Test
    void testDelete() {
        //根据主键删除
        studentRepository.deleteById("20200101007");
        assert !studentRepository.findById("20200101007").isPresent();
    }

    @Test
    void testFindAll() {
        studentRepository.save(new Student().setId("20200101007").setName("zbc").setGender(Student.Gender.MALE).setGrade(1));
        studentRepository.save(new Student().setId("20200101008").setName("cc").setGender(Student.Gender.FEMALE).setGrade(1));
        studentRepository.save(new Student().setId("20200101009").setName("gf").setGender(Student.Gender.MALE).setGrade(1));
        studentRepository.save(new Student().setId("20200101010").setName("mjyl").setGender(Student.Gender.FEMALE).setGrade(1));
        //查询全部记录
        List<Student> studentList = Lists.newArrayList(studentRepository.findAll());
        assert studentList.size() >0;
    }
}

使用RedisTemplate访问Redis

相对于 RedisRepository,RedisTemplate 更为灵活。RedisTemplate 基于 Redis 的原生命令封装了一系列的操作方法。这些方法基于 Redis 数据结构实现,Redis 的基础数据结构有 5 种:

1) String

字符串,Redis 中基础的数据结构。

2) List

列表,该数据结构底层的存储形式为链表。插入与删除的操作效率非常高。

3) Hash

哈希字典,与 Java 中的 HashMap 非常类似。底层结构为数组与链表。

4) Set

集合,与 Java 中的 HashSet 类似。内部的键值对是无序且唯一的。

5) Zset

有序集合,又称为 SortedSet。它一方面保证键值对唯一,另一方面会维护键值对的权重属性,为键值对进行排序。

操作 RedisTemplate 的示例代码如下:
@SpringBootTest
public class RedisTemplateTests {

    @Autowired
    RedisTemplate<String, Object> redisTemplate;

    @Test
    void teststring() {
        //设置键-值对
        redisTemplate.opsForValue().set("num", "123");
        //根据name获取值
        redisTemplate.opsForValue().get("num");
        //设置带有效期的键-值对
        redisTemplate.opsForValue().set("fade-num", "321", 10, TimeUnit.SECONDS);
        //10秒后结果为Null
        redisTemplate.opsForValue().get("fade-num");
    }

    @Test
    void testList() {
        //通过leftPush更新列表,也可以选择rightPush方法
        redisTemplate.opsForList().leftPush("languages", "java");
        redisTemplate.opsForList().leftPush("languages", "python");
        redisTemplate.opsForList().leftPush("languages", "c++");
        //查询列表长度
        assert redisTemplate.opsForList().size("languages") >= 3;
        //弹出列表左边的元素,之后结果在数据库中不复存在
        assert Objects.equals(redisTemplate.opsForList().leftPop("languages"), "c++");
        //弹出列表右边的元素,之后结果在数据库中不复存在
        assert Objects.equals(redisTemplate.opsForList().rightPop("languages"), "java");
    }

    @Test
    void testHash() {
        //更新hash第一个参数为hash的键,第二个参数为该hash内键-值对的键
        redisTemplate.opsForHash () .put ("hash", "red", "小红");
        redisTemplate.opsForHash () .put ("hash", "elephant", "小象");
        redisTemplate.opsForHash () .put ("hash", "red-elephant","小红象");
        //根据hash的键以及hash内键值对的键检索对应信息
        assert Objects.equals(redisTemplate.opsForHash().get("hash", "red"),"小红");
        //获取hash内所有键-值对的键
        Set<Object>hash = redisTemplate.opsForHash().keys("hash");
        hash.forEach(System.out::println);
    }

    @Test
    void testSet() {
        //新增set
        redisTemplate.opsForSet().add("set", "sir", "yes", "sir", "madam");
        //随机弹出一个元素
        redisTemplate.opsForSet().pop("set");
        //获取set中所有卤容
        redisTemplate.opsForSet().members("set").forEach(System.out::println);
    }

    @Test
    void testZSet() {
        //新增zset内容
        redisTemplate.opsForZSet().add("zset", "sir", -1);
        redisTemplate.opsForZSet().add("zset", "sir", 9);
        redisTemplate.opsForZSet().add("zset", "yes", 3);
        redisTemplate.opsForZSet().add("zset", "madam", 10);
        //获取该zset中sir的权重
        System.out.println(redisTemplate.opsForZSet().score("zset", "sir"));
        //根据权重区间遍历zset
        redisTemplate.opsForZSet().range("zset",0, 9).forEach(System.out::println);
    }
}

优秀文章