Spring Data Redis案例

wen java案例 2

本文目录导读:

Spring Data Redis案例

  1. 项目依赖配置 (pom.xml)
  2. 配置文件 (application.yml)
  3. Redis配置类
  4. 实体类
  5. 服务类 - 涵盖各种操作场景
  6. Controller 测试接口
  7. Redis工具类封装
  8. 异常处理建议
  9. 常见问题及解决方案

下面我给你一个完整的Spring Data Redis实战案例,包含配置、基础操作、序列化配置、以及一些常见的业务场景,这个案例会覆盖你开发中最常用到的功能。

项目依赖配置 (pom.xml)

<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>
<!-- JSON序列化工具 -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>

配置文件 (application.yml)

spring:
  redis:
    host: localhost
    port: 6379
    password:  # 如果没有密码可以为空
    database: 0
    timeout: 5000ms
    lettuce:
      pool:
        max-active: 8      # 最大连接数
        max-idle: 8        # 最大空闲连接
        min-idle: 0        # 最小空闲连接
        max-wait: -1ms     # 最大等待时间,-1表示不限制

Redis配置类

package com.example.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        // 使用StringRedisSerializer来序列化和反序列化redis的key
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // 使用GenericJackson2JsonRedisSerializer来序列化和反序列化redis的value
        GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        // 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;
    }
}

实体类

package com.example.entity;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
public class Product implements Serializable {
    private static final long serialVersionUID = 1L;
    private Long id;
    private String name;
    private String description;
    private BigDecimal price;
    private Integer stock;
    private LocalDateTime createTime;
}

服务类 - 涵盖各种操作场景

package com.example.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Service;
import com.example.entity.Product;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.*;
import java.util.concurrent.TimeUnit;
@Service
public class RedisService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private ObjectMapper objectMapper;
    // ==================== 1. String 操作 ====================
    /**
     * 设置字符串值
     */
    public void setString(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }
    /**
     * 设置带过期时间的字符串值
     */
    public void setStringWithExpire(String key, Object value, long timeout, TimeUnit unit) {
        redisTemplate.opsForValue().set(key, value, timeout, unit);
    }
    /**
     * 获取字符串值
     */
    public Object getString(String key) {
        return redisTemplate.opsForValue().get(key);
    }
    /**
     * 设置过期时间(秒)
     */
    public void expire(String key, long seconds) {
        redisTemplate.expire(key, seconds, TimeUnit.SECONDS);
    }
    // ==================== 2. Hash 操作 ====================
    /**
     * 设置Hash值
     */
    public void setHash(String key, String hashKey, Object value) {
        redisTemplate.opsForHash().put(key, hashKey, value);
    }
    /**
     * 获取Hash值
     */
    public Object getHash(String key, String hashKey) {
        return redisTemplate.opsForHash().get(key, hashKey);
    }
    /**
     * 批量设置Hash
     */
    public void setHashAll(String key, Map<String, Object> map) {
        redisTemplate.opsForHash().putAll(key, map);
    }
    /**
     * 获取整个Hash
     */
    public Map<Object, Object> getHashAll(String key) {
        return redisTemplate.opsForHash().entries(key);
    }
    // ==================== 3. List 操作 ====================
    /**
     * 向列表头部添加元素
     */
    public void pushToList(String key, Object value) {
        redisTemplate.opsForList().leftPush(key, value);
    }
    /**
     * 向列表尾部添加元素
     */
    public void addToList(String key, Object value) {
        redisTemplate.opsForList().rightPush(key, value);
    }
    /**
     * 批量添加元素到列表尾部
     */
    public void addAllToList(String key, List<Object> values) {
        redisTemplate.opsForList().rightPushAll(key, values);
    }
    /**
     * 获取列表元素(range查询)
     */
    public List<Object> getList(String key, long start, long end) {
        return redisTemplate.opsForList().range(key, start, end);
    }
    /**
     * 从列表中弹出头部元素
     */
    public Object popFromList(String key) {
        return redisTemplate.opsForList().leftPop(key);
    }
    // ==================== 4. Set 操作 ====================
    /**
     * 添加元素到Set
     */
    public void addToSet(String key, Object... values) {
        redisTemplate.opsForSet().add(key, values);
    }
    /**
     * 获取Set所有元素
     */
    public Set<Object> getSetMembers(String key) {
        return redisTemplate.opsForSet().members(key);
    }
    /**
     * 移除Set中的元素
     */
    public void removeFromSet(String key, Object... values) {
        redisTemplate.opsForSet().remove(key, values);
    }
    // ==================== 5. ZSet 操作 ====================
    /**
     * 添加元素到ZSet
     */
    public void addToZSet(String key, Object value, double score) {
        redisTemplate.opsForZSet().add(key, value, score);
    }
    /**
     * 获取ZSet中的元素(按分数排序)
     */
    public Set<Object> getZSetWithScore(String key, double min, double max) {
        return redisTemplate.opsForZSet().rangeByScore(key, min, max);
    }
    /**
     * 获取ZSet排名(正序,从小到大)
     */
    public Long getZSetRank(String key, Object value) {
        return redisTemplate.opsForZSet().rank(key, value);
    }
    // ==================== 6. 通用操作 ====================
    /**
     * 删除key
     */
    public Boolean delete(String key) {
        return redisTemplate.delete(key);
    }
    /**
     * 批量删除
     */
    public Long delete(List<String> keys) {
        return redisTemplate.delete(keys);
    }
    /**
     * 判断key是否存在
     */
    public Boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }
    /**
     * 获取key的剩余过期时间
     */
    public Long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }
    // ==================== 7. 业务场景:缓存商品信息 ====================
    /**
     * 缓存商品(存储整个对象)
     */
    public void cacheProduct(Product product) {
        String key = "product:" + product.getId();
        // 设置缓存,过期时间为2小时
        redisTemplate.opsForValue().set(key, product, 2, TimeUnit.HOURS);
    }
    /**
     * 获取缓存的商品
     */
    public Product getCachedProduct(Long productId) {
        String key = "product:" + productId;
        Object value = redisTemplate.opsForValue().get(key);
        if (value instanceof Product) {
            return (Product) value;
        }
        return null;
    }
    /**
     * 缓存商品并设置条件(如果key不存在才设置)
     */
    public Boolean cacheIfAbsent(String key, Product product) {
        return redisTemplate.opsForValue().setIfAbsent(key, product, 1, TimeUnit.HOURS);
    }
    // ==================== 8. 分布式锁示例 ====================
    /**
     * 尝试获取分布式锁
     */
    public Boolean tryLock(String lockKey, String requestId, long expireSeconds) {
        // 使用setIfAbsent实现分布式锁
        Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey, requestId);
        if (Boolean.TRUE.equals(result)) {
            // 设置过期时间
            redisTemplate.expire(lockKey, expireSeconds, TimeUnit.SECONDS);
        }
        return result;
    }
    /**
     * 释放分布式锁
     */
    public void releaseLock(String lockKey, String requestId) {
        // 为了防止误删,可以添加Lua脚本进行判断
        // 简单实现:直接删除(生产环境建议使用Lua脚本)
        String currentValue = (String) redisTemplate.opsForValue().get(lockKey);
        if (requestId.equals(currentValue)) {
            redisTemplate.delete(lockKey);
        }
    }
}

Controller 测试接口

package com.example.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import com.example.entity.Product;
import com.example.service.RedisService;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/redis")
public class RedisController {
    @Autowired
    private RedisService redisService;
    /**
     * 测试字符串操作
     */
    @PostMapping("/string")
    public String testString() {
        // 基础字符串
        redisService.setString("test:hello", "Hello Redis");
        Object value = redisService.getString("test:hello");
        // 带过期时间
        redisService.setStringWithExpire("test:expire", "This will expire in 5 seconds", 5, java.util.concurrent.TimeUnit.SECONDS);
        return "String测试结果: " + value;
    }
    /**
     * 测试对象缓存
     */
    @PostMapping("/product")
    public String testProduct() {
        // 创建商品
        Product product = new Product();
        product.setId(1L);
        product.setName("iPhone 15");
        product.setPrice(new BigDecimal("6999.00"));
        product.setStock(100);
        product.setCreateTime(LocalDateTime.now());
        // 缓存商品
        redisService.cacheProduct(product);
        // 获取商品
        Product cachedProduct = redisService.getCachedProduct(1L);
        if (cachedProduct != null) {
            return "缓存商品成功: " + cachedProduct.getName() + ", 价格: " + cachedProduct.getPrice();
        }
        return "缓存商品失败";
    }
    /**
     * 测试Hash操作 - 模拟用户信息
     */
    @PostMapping("/hash")
    public String testHash() {
        // 模拟用户信息
        Map<String, Object> userInfo = new HashMap<>();
        userInfo.put("name", "张三");
        userInfo.put("age", 25);
        userInfo.put("email", "zhangsan@example.com");
        // 存储为用户hash
        redisService.setHashAll("user:1001", userInfo);
        // 修改某个字段
        redisService.setHash("user:1001", "age", 26);
        // 获取整个hash
        Map<Object, Object> result = redisService.getHashAll("user:1001");
        return "Hash用户信息: " + result;
    }
    /**
     * 测试列表操作
     */
    @PostMapping("/list")
    public String testList() {
        // 创建一个任务队列
        for (int i = 1; i <= 5; i++) {
            redisService.addToList("task:queue", "Task-" + i);
        }
        // 获取列表长度
        Long listSize = redisService.getList("task:queue", 0, -1).size();
        // 弹出头部元素
        Object firstTask = redisService.popFromList("task:queue");
        // 查看剩余任务
        List<Object> remainingTasks = redisService.getList("task:queue", 0, -1);
        return "任务队列 - 初始大小: " + listSize + ", 弹出: " + firstTask + ", 剩余: " + remainingTasks;
    }
    /**
     * 测试删除
     */
    @DeleteMapping("/{key}")
    public String deleteKey(@PathVariable String key) {
        Boolean deleted = redisService.delete(key);
        return deleted ? "删除成功: " + key : "删除失败或key不存在: " + key;
    }
}

Redis工具类封装

package com.example.utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
 * Redis操作工具类
 */
@Component
public class RedisUtils {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    // ==================== 字符串操作 ====================
    public void set(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }
    public void set(String key, Object value, long timeout) {
        redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
    }
    public Object get(String key) {
        return redisTemplate.opsForValue().get(key);
    }
    public Boolean setIfAbsent(String key, Object value) {
        return redisTemplate.opsForValue().setIfAbsent(key, value);
    }
    // ==================== Hash操作 ====================
    public void hSet(String key, String hashKey, Object value) {
        redisTemplate.opsForHash().put(key, hashKey, value);
    }
    public Object hGet(String key, String hashKey) {
        return redisTemplate.opsForHash().get(key, hashKey);
    }
    public Map<Object, Object> hGetAll(String key) {
        return redisTemplate.opsForHash().entries(key);
    }
    // ==================== List操作 ====================
    public void lPush(String key, Object value) {
        redisTemplate.opsForList().leftPush(key, value);
    }
    public void rPush(String key, Object value) {
        redisTemplate.opsForList().rightPush(key, value);
    }
    public Object lPop(String key) {
        return redisTemplate.opsForList().leftPop(key);
    }
    public Object rPop(String key) {
        return redisTemplate.opsForList().rightPop(key);
    }
    // ==================== 通用操作 ====================
    public boolean delete(String key) {
        return Boolean.TRUE.equals(redisTemplate.delete(key));
    }
    public long delete(String... keys) {
        Long count = redisTemplate.delete(Arrays.asList(keys));
        return count != null ? count : 0;
    }
    public boolean hasKey(String key) {
        return Boolean.TRUE.equals(redisTemplate.hasKey(key));
    }
    public boolean expire(String key, long timeout) {
        return Boolean.TRUE.equals(redisTemplate.expire(key, timeout, TimeUnit.SECONDS));
    }
}

异常处理建议

package com.example.exception;
import org.springframework.data.redis.RedisConnectionFailureException;
import org.springframework.data.redis.serializer.SerializationException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(RedisConnectionFailureException.class)
    public String handleRedisConnectionFailure(RedisConnectionFailureException e) {
        return "Redis连接失败,请检查Redis服务是否启动: " + e.getMessage();
    }
    @ExceptionHandler(SerializationException.class)
    public String handleSerializationException(SerializationException e) {
        return "Redis序列化异常: " + e.getMessage();
    }
}

常见问题及解决方案

  1. 中文乱码问题:确保value序列化使用GenericJackson2JsonRedisSerializer或Jackson2JsonRedisSerializer。

  2. 对象序列化问题:实体类要实现Serializable接口。

  3. Redis连接池耗尽:合理配置连接池参数,设置max-wait时间。

  4. key设计规范:推荐使用"业务:模块:id"格式,如"product:info:1001"。

  5. 缓存穿透问题:使用setIfAbsent或者布隆过滤器。

这个案例覆盖了Spring Data Redis的大部分常用操作,你可以根据需要灵活使用,记得根据实际业务需求调整配置和操作方法!

抱歉,评论功能暂时关闭!