Spring Boot集成Redis与使用RedisTemplate进行基本数据结构操作示例

Spring Boot集成Redis

在Spring Boot项目中使用Redis时,需要如下几个步骤对Redis进行整合。

1. 加入Redis和Jedis客户端依赖。

        <!-- 配置Redis依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>lettuce-core</artifactId>
                    <groupId>io.lettuce</groupId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- 引入Jedis客户端依赖 -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>

2. 在Spring Boot中配置Redis。在application.properties配置文件中进行如下的配置:

# Spring Boot整合Redis
# 配置连接池属性
spring.redis.jedis.pool.min-idle=5
spring.redis.jedis.pool.max-active=10
spring.redis.jedis.pool.max-idle=10
spring.redis.jedis.pool.max-wait=2000
# 配置Redis服务器属性
spring.redis.port=6379
spring.redis.host=[REDIS_SERVER_IP]
# 在Redis没开启密码AUTH时无需设置
#spring.redis.password=[REDIS_SERVER_AUTH]
# Redis连接超时时间(毫秒)
spring.redis.timeout=1000

上面的配置文件中配置的Redis的连接池属性,用以连接Redis数据库。这样Spring Boot在自动装配时就会读取这里的配置来生成有关的Redis操作对象,例如RedisConnectionFactory、RedisTemplate、StringRedisTemplate常用的操作对象。其中RedisConnectionFactory对象的作用为:RedisConnectionFactory为RedisConnection对象创建的工厂方法,而RedisConnection对象是对Redis底层接口的封装,例如使用Jedis作为客户端时,将会提供RedisConnection接口的实现类JedisConnection对象用于连接Redis服务。

3. 配置Redis的序列化器。

RedisTemplate会默认使用JdkSerializationRedisSerializer进行序列化键值,这样Redis会将存入的对象序列化为一个特殊的字符串。为方便使用String类型和对象类型的的存储,下面对Redis中的序列化器进行一定的设置。创建如下RedisConfiguration配置类:

/**
 * SpringBoot中整合Redis时,设置redisTemplate中的序列化器
 *
 * @author yitian
 */
@Configuration
public class SpringRedisConfiguration {
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 利用Bean生命周期使用PostConstruct注解自定义后初始化方法
     */
    @PostConstruct
    public void init() {
        initRedisTemplate();
    }

    /**
     * 设置RedisTemplate的序列化器
     */
    private void initRedisTemplate() {
        RedisSerializer stringSerializer = redisTemplate.getStringSerializer();
        // 将Key和其散列表数据类型的filed都修改为使用StringRedisSerializer进行序列化
        redisTemplate.setKeySerializer(stringSerializer);
        redisTemplate.setHashKeySerializer(stringSerializer);
    }
}

这里使用Spring中Bean的生命周期特性中的@PostConstruct注解,定义后初始化方法。将RedisTemplate中的key序列化器和散列表的field都改为StringRedisSerializer, 这样在Redis服务器中得到的key和散列中的field就都是String类型的了。

以上操作即完成了Spring Boot项目中的Redis集成。

使用RedisTemplate进操作基本数据结构示例

通过以上的过程可以将Redis方便的集成到Spring Boot项目中,下面对Redis中的常用数据结构的操作进行示例说明,主要的数据类型包括:字符串(String)、散列表(Hash)、列表(List)、集合(Set)和有序集合(ZSet)。具体示例如下:

String操作示例

所有的操作示例均在RedisController类中,在通过相应的请求后,在IDEA的console中输出相应的示例结果:

@Controller
@RequestMapping("/redis")
public class RedisController {
    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * Redis字符串String操作示例
     */
    @GetMapping("/string")
    @ResponseBody
    public CommonResult redisStringOps() {
        // 向Redis中写入数据,形式为: <key, value>
        redisTemplate.opsForValue().set("key1", "value1");
        redisTemplate.opsForValue().set("int_key", "1");
        stringRedisTemplate.opsForValue().set("int", "1");
        stringRedisTemplate.opsForValue().increment("int", 5);

        System.out.println(redisTemplate.opsForValue().get("key1")); // output: value1
        System.out.println(redisTemplate.opsForValue().get("int_key")); // output: 1
        System.out.println(stringRedisTemplate.opsForValue().get("int")); // output: 6, 这里如果使用redisTemplate会存在NPE

        // RedisTemplate不支持-1操作,所以需要获取底层的Jedis连接进行操作
        Jedis jedis = (Jedis) stringRedisTemplate.getConnectionFactory().getConnection().getNativeConnection();
        jedis.decr("int");
        System.out.println(stringRedisTemplate.opsForValue().get("int")); // output: 5

        return new CommonResult(true, "见Console输出");
    }
}

Hash操作示例

/**
     * Redis散列表Hash操作示例
     */
    @GetMapping("/hash")
    @ResponseBody
    public CommonResult redisHashOps() {

        // 存入一个Hash类型数据,第一个为存入Hash数据结构的整体key,后面为对应存入的值,也就是如下结构:
        // Map<Key, Map<field, value>>
        Map<String, Object> hash = new HashMap<>();
        hash.put("field1", "value1");
        hash.put("field2", "value2");
        stringRedisTemplate.opsForHash().putAll("hash", hash);
        System.out.println(stringRedisTemplate.opsForHash().get("hash", "field1")); // output: value1

        // 向key为hash的散列表中存入单条数据
        stringRedisTemplate.opsForHash().put("hash", "field3", "value3");
        System.out.println(stringRedisTemplate.opsForHash().get("hash", "field3")); // output: value3

        // 绑定散列表Hash进行操作,可以对一个hash进行多次操作
        BoundHashOperations hashOps = stringRedisTemplate.boundHashOps("hash");
        hashOps.put("field4", "value4");
        hashOps.delete("field4");

        // 输出名为hash散列表中的所有key和value
        System.out.println(hashOps.keys()); // output: [filed, field1, field2, field3]
        System.out.println(hashOps.values()); // output: [hvalue, value1, value2, value3]

        // 创建另一个散列表进行操作
        stringRedisTemplate.opsForHash().put("hash1", "hash1_key1", "hash1_value1");
        System.out.println(stringRedisTemplate.opsForHash().get("hash1", "hash1_key1")); // output: hash1_value1

        return new CommonResult(true, "见Console输出");
    }

List操作示例

/**
     * Redis列表操作示例
     * List为链表结构,查询性能低,插入性能高
     */
    @GetMapping("/list")
    @ResponseBody
    public CommonResult redisListOps() {
        // 在开始操作前判断并删除已存在的list,否则下面的过程会对同一个list进行累计操作
        deleteExistedList("list1");
        deleteExistedList("list2");

        // 从左插入一个链表,名为list1,顺序为,v8, v6, v4, v2
        stringRedisTemplate.opsForList().leftPushAll("list1", "v2", "v4", "v6", "v8");
        printRedisList("list1"); // List: list1, elements: v8, v6, v4, v2,

        // 从右侧插入一个列表,名为list2,顺序为, v1, v3, v7, v9
        stringRedisTemplate.opsForList().rightPushAll("list2", "v1", "v3", "v7", "v9");
        printRedisList("list2"); // List: list2, elements: v1, v3, v7, v9,

        // pop方法会将list中的元素弹出后删除
        System.out.println(stringRedisTemplate.opsForList().leftPop("list1")); // v8
        System.out.println(stringRedisTemplate.opsForList().rightPop("list1")); // v2
        System.out.println(stringRedisTemplate.opsForList().leftPop("list2")); // v1
        System.out.println(stringRedisTemplate.opsForList().rightPop("list2")); // v9

        // 使用BoundOperations绑定名为list2的列表进行多次操作
        BoundListOperations listOps = stringRedisTemplate.boundListOps("list2");
        // 从list2右侧输出一个元素
        Object result1 = listOps.rightPop();
        System.out.println(result1); // v7

        // 输出list2中index=1的元素,(List中从0开始)
        Object result2 = listOps.index(1);
        System.out.println(result2); // null,因为此时list2经过上述的pop操作只剩下一个元素

        // 从左侧向list2中加入一个元素为v0
        listOps.leftPush("v0");

        // 输出List2中元素的总个数
        Long size = listOps.size();
        System.out.println(size); // 2

        // 取出list2中【0,size-1】范围内的所有元素
        List elements = listOps.range(0, size - 1);

        System.out.println(elements); // [v0, v3]
        // 测试range方法是否会pop出list中的元素,答案:不会,list中的元素以复制的方式进行返回
        System.out.println(listOps.size());

        return new CommonResult(true, "见Console输出");
    }

其中使用到的一些公用方法如下:

/**
     * 使用List中的range方法来获取list中的所有元素
     * range方法不会像pop方法一样将元素从redis中删除
     */
    private void printRedisList(String listKey) {
        BoundListOperations listOps = stringRedisTemplate.boundListOps(listKey);
        Long size = listOps.size();
        List elemetns = listOps.range(0, size - 1);

        StringBuilder sb = new StringBuilder();
        sb.append("List: " + listKey + ", elements: ");
        for (int i = 0; i < elemetns.size(); i++) {
            sb.append(elemetns.get(i) + ", ");
        }
        System.out.println(sb.toString());
    }

    /**
     * 判断list是否存在并进行删除
     */
    private void deleteExistedList(String listKey) {
        if (stringRedisTemplate.opsForList().size(listKey) != null) {
            stringRedisTemplate.delete(listKey);
        }
    }

 Set操作示例

/**
     * Redis中无序集合操作Set,元素无序且不能重复
     */
    @GetMapping("/set")
    @ResponseBody
    public CommonResult redisSetOps() {
        // 再开始操作set之前,判断并清空set集合中的元素
        emptySet("set1");
        emptySet("set2");

        // 分别向set1和set2集合添加元素
        stringRedisTemplate.opsForSet().add("set1", "v1", "v1", "v2", "v3", "v4", "v5");
        stringRedisTemplate.opsForSet().add("set2", "v2", "v4", "v6", "v8");
        System.out.println(stringRedisTemplate.opsForSet().members("set1")); // [v4, v2, v3, v1, v5]
        System.out.println(stringRedisTemplate.opsForSet().members("set2")); // [v6, v4, v2, v8]

        // 绑定一个set1集合进行多次操作
        BoundSetOperations setOps = stringRedisTemplate.boundSetOps("set1");
        // 添加元素
        setOps.add("v6", "v7");
        // 删除集合中的元素
        setOps.remove("v1", "v7");
        System.out.println(setOps.members()); // [v2, v5, v3, v6, v4]
        System.out.println(setOps.size()); // 5

        // 求交集
        Set inter = setOps.intersect("set2");
        System.out.println(inter); // [v6, v4, v2]

        // 求差集
        Set diff = setOps.diff("set2");
        System.out.println(diff); // [v5, v3]
        // 测试求差集后是否会影响原set中的元素
        System.out.println(setOps.members()); // [v2, v5, v3, v6, v4]

        // 求差集并使用新的集合保存
        setOps.diffAndStore("set2", "diff1");
        printRedisList("diff1");

        // 求并集
        Set union = setOps.union("set2");
        System.out.println(union); // [v2, v5, v3, v6, v4, v8]

        // 求并集并使用新的集合保存
        setOps.unionAndStore("set2", "union1");
        printSetMembers("union1");

        return new CommonResult(true, "见Console输出");
    }

其中使用到的一些公用方法如下:

    /**
     * 清空已存在的set集合,避免多次操作过程中历史数据对示例操作的影响
     */
    private void emptySet(String setKey) {
        if (!stringRedisTemplate.opsForSet().members(setKey).isEmpty()) {
            stringRedisTemplate.opsForSet().members(setKey).clear();
        }
    }

    /**
     * 打印set结合中的所有元素
     */
    private void printSetMembers(String setKey) {
        BoundSetOperations setOps = stringRedisTemplate.boundSetOps(setKey);
        System.out.println(setOps.members());
    }

ZSet操作示例

    /**
     * Redis中ZSet集合示例操作
     * Zset集合根据score值进行排序,所以每个元素的结构为/<value, score>
     */
    @GetMapping("/zset")
    @ResponseBody
    public CommonResult redisZsetOps() {

        // 使用TypedTuple创建ZSET
        Set<ZSetOperations.TypedTuple<String>> typedTuples = new HashSet<>();
        for (int i = 1; i <= 9; i++) {
            double score = i * 0.1;
            ZSetOperations.TypedTuple<String> typedTuple = new DefaultTypedTuple<>("value" + i, score);
            typedTuples.add(typedTuple);
        }
        stringRedisTemplate.opsForZSet().add("zset1", typedTuples);

        // 输出Zset的集合大小
        BoundZSetOperations zsetOps = stringRedisTemplate.boundZSetOps("zset1");
        Long size = zsetOps.size();
        System.out.println(size); // 8

        // 输出Zset结合中的所有元素
        Set members = zsetOps.range(0, size - 1);
        System.out.println(members); // [value1, value2, value3, value4, value5, value6, value7, value8]

        // 加入一个元素
        zsetOps.add("value10", 0.26);

        // 输出score在该范围中的所有元素
        Set setScore = zsetOps.rangeByScore(0.2, 0.6);
        System.out.println(setScore); // [value2, value10, value3, value4, value5]

        // 定义值范围
        RedisZSetCommands.Range range = new RedisZSetCommands.Range();
//        range.gt("value3"); // 大于value3
//        range.lt("value8"); // 小于value8
        range.lte("value8"); // 小于等于value8
        // 按值排序
        Set setLex = zsetOps.rangeByLex(range);
        System.out.println(setLex); // [value1, value2, value10, value3, value4, value5, value6, value7, value8]

        // 删除元素
        zsetOps.remove("value9", "value2");
        System.out.println(zsetOps.range(0, zsetOps.size() - 1)); // [value1, value10, value3, value4, value5, value6, value7, value8]

        // 得到value值对应的分数
        Double score = zsetOps.score("value8");
        System.out.println(score); // 0.8

        // 在下标区间内,按分数排序,同时返回value和score
        Set<ZSetOperations.TypedTuple<String>> rangeSet = zsetOps.rangeWithScores(1, 6);
        System.out.println(rangeSet.toArray());

        // 在分数区间内,按分数排序,同时返回value和score
        Set<ZSetOperations.TypedTuple<String>> scoreSet = zsetOps.rangeByScoreWithScores(1, 6);
        System.out.println(scoreSet); // []

        // 从大到小排序,默认为从小到达排序
        Set<String> reverseSet = zsetOps.reverseRange(2, 8);
        System.out.println(reverseSet); // [value6, value5, value4, value3, value10, value1]

        return new CommonResult(true, "见Console输出");    
    }
发布了296 篇原创文章 · 获赞 35 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/yitian_z/article/details/104265069