SpringBoot操作Redis缓存

SpringBoot操作Redis缓存

Redis有很多使用场景,一个使用场景就是缓存数据库的数据。Redis作为一个内存数据库,存取数据的速度比传

统的数据库快得多。使用Redis缓存数据库数据,可以减轻系统对数据库的访问压力,及加快查询效率等好处。下

面讲解如何使用 SpringBoot + Redis来缓存数据库数据(这里数据库使用MySql)。

Spring支持多种缓存技术:RedisCacheManagerEhCacheCacheManagerGuavaCacheManager等,使用之

前需要配置一个CacheManager的Bean。配置好之后使用三个注解来缓存数据:@Cacheable@CachePut

@CacheEvict。这三个注解可以加Service层或Dao层的类名上或方法上(建议加在Service层的方法上),加上类上

表示所有方法支持该注解的缓存;三注解需要指定Key,以返回值作为value操作缓存服务。所以,如果加在Dao

层,当新增1行数据时,返回数字1,会将1缓存到Redis,而不是缓存新增的数据。

1、使用的数据库脚本

create database redis_cache_test;
use redis_cache_test;
CREATE TABLE `sys_user` (
  `t_id` varchar(32) NOT NULL COMMENT 'ID编号',
  `t_name` varchar(300) DEFAULT NULL COMMENT '用户姓名',
  `t_age` int(11) DEFAULT NULL COMMENT '用户年龄',
  PRIMARY KEY (`t_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `sys_user` VALUES ('ID0001', 'zsx1', '27');
INSERT INTO `sys_user` VALUES ('ID0002', 'zsx2', '27');
INSERT INTO `sys_user` VALUES ('ID0003', 'zsx3', '27');
INSERT INTO `sys_user` VALUES ('ID0004', 'zsx4', '27');
INSERT INTO `sys_user` VALUES ('ID0005', 'zsx5', '18');
INSERT INTO `sys_user` VALUES ('ID0006', 'zsx6', '12');
INSERT INTO `sys_user` VALUES ('ID0007', 'zsx7', '8');

2、引入pom依赖


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0modelVersion>
    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.4.5version>
        <relativePath/>
    parent>
    <groupId>com.examplegroupId>
    <artifactId>spring-boot-redis-cacheartifactId>
    <version>0.0.1-SNAPSHOTversion>
    <name>spring-boot-redis-cachename>
    <description>spring-boot-redis-cachedescription>

    <properties>
        <java.version>1.8java.version>
    properties>

    <dependencies>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starterartifactId>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-data-redisartifactId>
        dependency>

        <dependency>
            <groupId>org.mybatis.spring.bootgroupId>
            <artifactId>mybatis-spring-boot-starterartifactId>
            <version>2.0.1version>
        dependency>

        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <scope>runtimescope>
        dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.coregroupId>
            <artifactId>jackson-coreartifactId>
            <version>2.9.6version>
        dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.coregroupId>
            <artifactId>jackson-annotationsartifactId>
            <version>2.9.6version>
        dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.coregroupId>
            <artifactId>jackson-databindartifactId>
            <version>2.9.6version>
        dependency>

        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
        dependency>

        <dependency>
            <groupId>junitgroupId>
            <artifactId>junitartifactId>
            <scope>testscope>
        dependency>

    dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-maven-pluginartifactId>
            plugin>
        plugins>
    build>

project>

3、配置文件

# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.jedis.pool.max-active=10
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=-1ms
# 连接池中的最大空闲连接
spring.redis.jedis.pool.max-idle=10
# 连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=1000ms

logging.level.root=ERROR
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/redis_cache_test?characterEncoding=utf8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root

4、实体类User

package com.example.springbootrediscache.redis;

/**
 * 简单的bean,对应DB的表
 */
public class User {

    public User() {
    }

    public User(String id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "User [id=" + id + ", name=" + name + ", age=" + age + "]";
    }

    private String id;
    private String name;
    private int age;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

5、UserDao

package com.example.springbootrediscache.redis;

import org.apache.ibatis.annotations.*;
import org.springframework.stereotype.Component;

import java.util.List;

@Mapper
@Component
public interface UserDao {
    /**
     * 插入数据
     *
     * @param bean
     * @return
     */
    @Insert("insert into sys_user       "
            + "(t_id, t_name, t_age)    "
            + "values                   "
            + "(#{id}, #{name}, ${age}) ")
    int insertUser(User bean);

    /**
     * 查询所有数据
     *
     * @return
     */
    @ResultMap("redisUserDaoResults")
    @Select("select t_id, t_name, t_age "
            + "from sys_user            ")
    List<User> selectUser();

    /**
     * 根据id查询数据
     *
     * @param id
     * @return
     */
    @Select("select t_id, t_age, t_name  "
            + "from sys_user             "
            + "where t_id = #{id}        ")
    @Results(id = "redisUserDaoResults", value = {
            @Result(property = "id", column = "t_id"),
            @Result(property = "age", column = "t_age"),
            @Result(property = "name", column = "t_name"),
    })
    User selectUserById(@Param("id") String id);

    /**
     * 根据id修改数据
     *
     * @param user
     * @return
     */
    @Update("update sys_user set  "
            + "t_name = #{name},  "
            + "t_age  = #{age}    "
            + "where t_id = #{id} ")
    int updateUser(User user);

    /**
     * 根据id删除数据
     *
     * @param id
     * @return
     */
    @Delete("delete from sys_user  "
            + "where t_id = #{id}  ")
    int deleteUserById(@Param("id") String id);

}

6、RedisCacheUserDao

可以在Dao和Service任何一处使用缓存

package com.example.springbootrediscache.redis;

import org.apache.ibatis.annotations.*;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
@Mapper
@CacheConfig(cacheNames = "users")
public interface RedisCacheUserDao {

    // https://blog.csdn.net/f641385712/article/details/95169002
    @Cacheable(key = "#a0")
    @Select("select t_id, t_age, t_name  "
            + "from sys_user             "
            + "where t_id = #{id}        ")
    @Results(id = "redisUserDaoResults", value = {
            @Result(property = "id", column = "t_id"),
            @Result(property = "age", column = "t_age"),
            @Result(property = "name", column = "t_name"),
    })
    User selectUserById(@Param("id") String id);

    @Cacheable(key = "'list'")
    @ResultMap("redisUserDaoResults")
    @Select("select t_id, t_name, t_age "
            + "from sys_user            ")
    List<User> selectUser();
}

7、RedisCacheUserService

package com.example.springbootrediscache.redis;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * 指定默认缓存区
 * 缓存区:key的前缀,与指定的key构成redis的key,如 user::10001
 */
@CacheConfig(cacheNames = "user")
@Service
public class RedisCacheUserService {

    @Autowired
    private UserDao dao;

    /**
     * @Cacheable 缓存有数据时,从缓存获取;没有数据时,将返回值保存到缓存中
     * @Cacheable 一般在查询中使用
     * 1) cacheNames 指定缓存区,没有配置使用@CacheConfig指定的缓存区
     * 2) key 指定缓存区的key
     * 3) 注解的值使用SpEL表达式
     * eq ==
     * lt <
     * le <=
     * gt >
     * ge >=
     */
    @Cacheable(cacheNames = "user", key = "#id")
    public User selectUserById(String id) {
        return dao.selectUserById(id);
    }

    @Cacheable(key = "'list'")
    public List<User> selectUser() {
        return dao.selectUser();
    }

    /**
     * condition 满足条件缓存数据
     */
    @Cacheable(key = "#id", condition = "#number ge 20") // >= 20
    public User selectUserByIdWithCondition(String id, int number) {
        return dao.selectUserById(id);
    }

    /**
     * unless 满足条件时否决缓存数据
     */
    @Cacheable(key = "#id", unless = "#number lt 20") // < 20
    public User selectUserByIdWithUnless(String id, int number) {
        return dao.selectUserById(id);
    }

    /**
     * @CachePut 将返回值保存到缓存中
     * @CachePut 一般在新增和修改中使用
     */
    @CachePut(key = "#user.id")
    public User insertUser(User user) {
        dao.insertUser(user);
        return user;
    }

    @CachePut(key = "#user.id", condition = "#user.age ge 20")
    public User insertUserWithCondition(User user) {
        dao.insertUser(user);
        return user;
    }

    @CachePut(key = "#user.id")
    public User updateUser(User user) {
        dao.updateUser(user);
        return user;
    }

    /**
     * 根据key删除缓存区中的数据
     */
    @CacheEvict(key = "#id")
    public void deleteUserById(String id) {
        dao.deleteUserById(id);
    }

    /**
     * allEntries = true :删除整个缓存区的所有值,此时指定的key无效
     * beforeInvocation = true :默认false,表示调用方法之后删除缓存数据;true时,在调用之前删除缓存数据(如方法出现异常)
     */
    @CacheEvict(key = "#id", allEntries = true)
    public void deleteUserByIdAndCleanCache(String id) {
        dao.deleteUserById(id);
    }
}

8、RedisConfig

RedisCacheManager的配置如下:

package com.example.springbootrediscache.redis;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheManager.RedisCacheManagerBuilder;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;

@Configuration
@EnableCaching
@Slf4j
public class RedisConfig {
    /**
     * SpringBoot配置redis作为默认缓存工具
     * SpringBoot 2.0 以上版本的配置
     */
    @Bean
    public CacheManager cacheManager(RedisTemplate<String, Object> template) {
        RedisCacheConfiguration defaultCacheConfiguration =
                RedisCacheConfiguration
                        .defaultCacheConfig()
                        // 设置key为String
                        .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(template.getStringSerializer()))
                        // 设置value为自动转Json的Object
                        .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(template.getValueSerializer()))
                        // 不缓存null
                        .disableCachingNullValues()
                        // 缓存数据保存1小时
                        .entryTtl(Duration.ofHours(1));
        RedisCacheManager redisCacheManager =
                RedisCacheManagerBuilder
                        // Redis 连接工厂
                        .fromConnectionFactory(template.getConnectionFactory())
                        // 缓存配置
                        .cacheDefaults(defaultCacheConfiguration)
                        // 配置同步修改或删除 put/evict
                        .transactionAware()
                        .build();
        return redisCacheManager;
    }

    /**
     * redis template
     */
    @Bean(name = "template")
    public RedisTemplate<String, Object> template(RedisConnectionFactory factory) {
        log.info("调用自定义的Redis Template!!!");
        // 创建RedisTemplate对象
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        // 配置连接工厂
        template.setConnectionFactory(factory);
        // 定义Jackson2JsonRedisSerializer序列化对象
        Jackson2JsonRedisSerializer<Object> jacksonSeial = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper om = new ObjectMapper();
        // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会报异常
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jacksonSeial.setObjectMapper(om);
        StringRedisSerializer stringSerial = new StringRedisSerializer();
        // redis key 序列化方式使用stringSerial
        template.setKeySerializer(stringSerial);
        // redis value 序列化方式使用jackson
        template.setValueSerializer(jacksonSeial);
        // redis hash key 序列化方式使用stringSerial
        template.setHashKeySerializer(stringSerial);
        // redis hash value 序列化方式使用jackson
        template.setHashValueSerializer(jacksonSeial);
        template.afterPropertiesSet();
        return template;
    }

    /**
     * 定义数据类型
     * string hash list set zset
     */
    /**
     * redis string
     */
    @Bean
    public ValueOperations<String, Object> valueOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForValue();
    }

    /**
     * redis hash
     */
    @Bean
    public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForHash();
    }

    /**
     * redis list
     */
    @Bean
    public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForList();
    }

    /**
     * redis set
     */
    @Bean
    public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForSet();
    }

    /**
     * redis zset
     */
    @Bean
    public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForZSet();
    }
}

9、启动类

package com.example.springbootrediscache;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringBootRedisCacheApplication {

	public static void main(String[] args) {

		SpringApplication.run(SpringBootRedisCacheApplication.class, args);
	}

}

10、测试

mysql> select * from sys_user;
+--------+--------+-------+
| t_id   | t_name | t_age |
+--------+--------+-------+
| ID0001 | zsx1   |    27 |
| ID0002 | zsx2   |    27 |
| ID0003 | zsx3   |    27 |
| ID0004 | zsx4   |    27 |
| ID0005 | zsx5   |    18 |
| ID0006 | zsx6   |    12 |
| ID0007 | zsx7   |     8 |
+--------+--------+-------+
7 rows in set

10.1 RedisDaoCache

package com.example.springbootrediscache;

import com.example.springbootrediscache.redis.RedisCacheUserDao;
import com.example.springbootrediscache.redis.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;

@SpringBootTest(classes = SpringBootRedisCacheApplication.class)
@RunWith(SpringRunner.class)
public class RedisDaoCache {

    @Autowired
    private RedisCacheUserDao dao;

    @Autowired
    private ValueOperations<String, Object> redisString;

    @Test
    public void test001() {
        System.out.println("redis before use : " + redisString.get("users::ID0001"));
        System.out.println(dao.selectUserById("ID0001"));
        System.out.println("redis after use : " + redisString.get("users::ID0001"));
    }

    @Test
    public void test002() {
        System.out.println(redisString.get("users::list"));
        List<User> list = dao.selectUser();
        System.out.println(list.size());
        System.out.println(redisString.get("users::list"));
    }
}
@Test
public void test001() {
	System.out.println("redis before use : " + redisString.get("users::ID0001"));
	System.out.println(dao.selectUserById("ID0001"));
	System.out.println("redis after use : " + redisString.get("users::ID0001"));
}
redis before use : null
User [id=ID0001, name=zsx1, age=27]
redis after use : User [id=ID0001, name=zsx1, age=27]
@Test
public void test002() {
	System.out.println(redisString.get("users::list"));
	List<User> list = dao.selectUser();
	System.out.println(list.size());
	System.out.println(redisString.get("users::list"));
}
null
7
[User [id=ID0001, name=zsx1, age=27], User [id=ID0002, name=zsx2, age=27], User [id=ID0003, name=zsx3, age=27], User [id=ID0004, name=zsx4, age=27], User [id=ID0005, name=zsx5, age=18], User [id=ID0006, name=zsx6, age=12], User [id=ID0007, name=zsx7, age=8]]

10.2 RedisServiceCache

package com.example.springbootrediscache;

import com.example.springbootrediscache.redis.RedisCacheUserService;
import com.example.springbootrediscache.redis.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;

@SpringBootTest(classes = SpringBootRedisCacheApplication.class)
@RunWith(SpringRunner.class)
public class RedisServiceCache {

    @Autowired
    private RedisCacheUserService service;

    @Autowired
    private ValueOperations<String, Object> redisString;

    /**
     * 测试 @Cacheable 注解,缓存bean
     */
    @Test
    public void test001() {
        // redis before use : null
        // User [id=ID0001, name=zsx, age=27]
        // redis after use : User [id=ID0001, name=zsx, age=27]
        System.out.println("redis before use : " + redisString.get("user::ID0001"));
        System.out.println(service.selectUserById("ID0001"));
        System.out.println("redis after use : " + redisString.get("user::ID0001"));
        System.out.println();
        // redis before use : User [id=ID0001, name=zsx, age=27]
        // User [id=ID0001, name=zsx, age=27]
        // redis after use : User [id=ID0001, name=zsx, age=27]
        System.out.println("redis before use : " + redisString.get("user::ID0001"));
        System.out.println(service.selectUserById("ID0001"));
        System.out.println("redis after use : " + redisString.get("user::ID0001"));
        System.out.println();
    }

    /**
     * 测试 @Cacheable 注解,缓存list
     * 'list':指定 list字符串作为key
     */
    @Test
    public void test002() {
        // null
        // 3
        // [User [id=ID0001, name=zsx1, age=27], User [id=ID0002, name=zsx2, age=27], User [id=ID0003, name=zsx3, age=27]]
        System.out.println(redisString.get("user::list"));
        // 如果往数据库中插入数据,依然还是会从缓存中拿出,不会拿出最新的数据
        List<User> list = service.selectUser();
        System.out.println(list.size());
        System.out.println(redisString.get("user::list"));
    }

    /**
     * 测试 @Cacheable 注解的 condition : 满足条件时缓存数据
     */
    @Test
    public void test003() {
        // User [id=ID0002, name=zsx2, age=27]
        // redis data[ID0002] : null
        User user1 = service.selectUserByIdWithCondition("ID0002", 19);
        System.out.println(user1);
        System.out.println("redis data[ID0002] : " + redisString.get("user::ID0002"));
        // User [id=ID0003, name=zsx3, age=27]
        // redis data[ID0003]: User [id=ID0003, name=zsx3, age=27]
        User user2 = service.selectUserByIdWithCondition("ID0003", 20);
        System.out.println(user2);
        System.out.println("redis data[ID0003]: " + redisString.get("user::ID0003"));
    }

    /**
     * 测试 @Cacheable 注解的 unless : 满足条件时不缓存数据
     */
    @Test
    public void test004() {
        // User [id=ID0004, name=zsx4, age=27]
        // redis data[ID0004] : null
        User user1 = service.selectUserByIdWithUnless("ID0004", 19);
        System.out.println(user1);
        System.out.println("redis data[ID0004] : " + redisString.get("user::ID0004"));
        // User [id=ID0005, name=zsx5, age=18]
        // redis data[ID0005]: User [id=ID0005, name=zsx5, age=18]
        User user2 = service.selectUserByIdWithUnless("ID0005", 20);
        System.out.println(user2);
        System.out.println("redis data[ID0005]: " + redisString.get("user::ID0005"));
    }

    /**
     * 测试 @CachePut 注解
     */
    @Test
    public void test005() {
        User user = new User("10086", "insert_name", 11);
        service.insertUser(user);
        // User [id=10086, name=insert_name, age=11]
        System.out.println(redisString.get("user::10086"));

        User user2 = new User("10087", "insert_name", 22);
        service.insertUserWithCondition(user2);
        // User [id=10087, name=insert_name, age=22]
        System.out.println(redisString.get("user::10087"));

        User user3 = new User("10086", "update_name", 12);
        service.updateUser(user3);
        // User [id=10086, name=update_name, age=12]
        System.out.println(redisString.get("user::10086"));
    }

    /**
     * 测试 @CacheEvict 注解
     */
    @Test
    public void test006() {
        // [user::ID0003, user::ID0001, user::10086, user::list, user::10087, user::ID0005]
        System.out.println(redisString.getOperations().keys("user::*"));
        service.deleteUserById("10086");
        // [user::ID0003, user::ID0001, user::list, user::10087, user::ID0005]
        System.out.println(redisString.getOperations().keys("user::*"));
        service.deleteUserByIdAndCleanCache("10087");
        // []
        System.out.println(redisString.getOperations().keys("user::*"));
    }
}

你可能感兴趣的:(redis,redis,spring,boot)