Springboot集成redis (使用注解)

本文涉及的产品
云原生内存数据库 Tair,内存型 2GB
云数据库 Redis 版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: Springboot集成redis (使用注解),在一定程度上能够方便开发

springboot集成redis (使用注解)

注意:一般开发中小型快速应用,适合redis注解开发。但是想要合理点的设置缓存,建议还是手动配置

为什么要使用注解版?

  1. 注解版使用方便
  2. 注解版功能多样化,适合多种环境

哪种不适合缓存

  1. insert插入数据库后,返回一个int值,这个值有必要缓存???

    没必要。因为一般情况下我不会从缓存中取出int值,例如我插入了一个数据,缓存一个int值,在再插入一个数据,这种缓存一般不会被使用。

    而且插入一条数据后,就应该让该命名空间下的所有key全部移除,所以一般写的是@CacheEvict(value allEntries=true)

  • @EnableCache 开启基于注解的缓存功能
  • @Cacheable注解 :先从redis数据库中 按照当前key查找,有没有。如果redis中有,是不会走当前该方法的,如果没有再调用方法返回结果,如果结果不为null将其缓存到数据库中(一般用于find)

    • 属性:
    • value:key的一部分(前缀),主要是指明数据放在那个key范围
    • key:key的主体,#p0:指明取出第一个参数 #p1:指明取出第二个参数。。。依此类推
    • unless:结果为true,将当前的数据结果不保存到redis,#result:指明取出数据库中返回的结果
    • condition 结果如果为true,将当前数据保存到redis
    • 实例

      @Cacheable(value = RedisConfig.REDIS_DATABASE_KEY, key = "'user-'+#p0",  unless = "#result==null")
          @Override
          public User getUserByName(String name) {
              UserExample example = new UserExample();
              example.createCriteria().andNameEqualTo(name);
              List<User> users = userMapper.selectByExample(example);
              if(users.size()==0){
                  return null;
              }
              return users.get(0);
          }
  • @CachePut: 主要用于向数据库中插入数据,向数据中插入数据的时候,会将返回的int类型,放入redis中缓存,当然是有选择性的(一般用于insert)
  • 属性:
  • value:key的一部分,命名空间
  • key:指定key的名称
  • unless:满足条件,则不将返回的结果放入redis
  • condition: 满足条件,则将返回的结果放入redis
  • 实例

    @CachePut(value = RedisConfig.REDIS_DATABASE_KEY,key = "'user-insert-'+#p0.id",unless = "#result==0")
        @Override
        public int insert(User record) {
            return userMapper.insert(record);
        }
  • @CacheEvict:满足条件则移除当前key在redis中的数据(一般用于update/delete)

    • 属性:
    • value: 同理命名空间
    • key: key名称
    • condition:满足什么条件从缓存中移除指定的key
    • AllEntries:true/false 是否移除命名空间下的所有key
    • 实例

      @CacheEvict(value = RedisConfig.REDIS_DATABASE_KEY,key = "'user-'+#p0.id",condition = "#result==1")
          @Override
          public int updateByPrimaryKey(User record) {
              return userMapper.updateByPrimaryKey(record);
          }
  • 问题

    我们知道如果进行查询的时候,会将数据缓存到redis中。一旦进行增删改,那么原本数据库中的数据可能会发生变化,那么增删改成功后,应该要指定的移除指定key的缓存。但是我们通过@CaheEvict移除指定key的数据,发现并不能行的通。

    为什么?

    例如:我们修改了一个product信息,那么应该移除product-id 这种key的数据,但是如果这个product数据关联,product-list等等这种key(即list中有当前修改前的数据),按理说我们应该移除这些相关联的key

    怎么做

    1. 第一种方法

      @CaheEvict(value=“nameSpace”,allEntries=true),移除这个命名空间下的所有数据

      @CaheEvict(value=“product”,allEntries=true),移除product命名空间下的所有keys

    2. 第二种

      通过redisTemplate手动移除相关联的key的数据(相对来说麻烦点)

  • 完整配置

    1. pom.xml

      <!--springboot中的redis依赖-->
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-data-redis</artifactId>
      </dependency>
      <!-- lettuce pool 缓存连接池-->
      <dependency>
          <groupId>org.apache.commons</groupId>
          <artifactId>commons-pool2</artifactId>
      </dependency>
    2. redisConfig

      //开启基于注解的配置
      @EnableCaching
      @Configuration
      public class RedisConfig {
      
          /**
           * redis数据库自定义key的命名空间
           */
          public static final String REDIS_DATABASE_KEY="tmall_springboot";
          public static final String REDIS_CATEGORY_KEY="category";
          public static final String REDIS_ORDER_ITEM_KEY="orderItem";
          public static final String REDIS_ORDER_KEY="order";
          public static final String REDIS_PRODUCT_KEY="product";
          public static final String REDIS_PROPERTY_KEY="property";
          public static final String REDIS_PROPERTY_VALUE_KEY="propertyValue";
          public static final String REDIS_REVIEW_KEY="review";
          public static final String REDIS_USER_KEY="user";
          public static final String REDIS_PRODUCT_IMAGE_KEY="productImage";
      
      
          /**
           * 自动配置的redisTemplate,存在序列化问题,会导致存入redis数据库中的数据,不容易看清所以需要自己配置
           */
          /**
           * 配置自定义redisTemplate
           * @return
           */
          @Bean
          RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory,RedisSerializer redisSerializer) {
      
              RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
      
              redisTemplate.setConnectionFactory(redisConnectionFactory);
      
              // 设置键(key)的序列化采用StringRedisSerializer。
              redisTemplate.setKeySerializer(new StringRedisSerializer());
              // 设置值(value)的序列化采用Jackson2JsonRedisSerializer。
              redisTemplate.setValueSerializer(redisSerializer);
              //  设置hashKey的序列化
              redisTemplate.setHashKeySerializer(new StringRedisSerializer());
              redisTemplate.setHashValueSerializer(redisSerializer);
              redisTemplate.afterPropertiesSet();
              return redisTemplate;
          }
      
          /**
           * 配置json序列化器(我们使用jackson的序列化器)
           */
          @Bean
          public RedisSerializer redisSerializer(){
      
              Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
              ObjectMapper objectMapper = new ObjectMapper();
              objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
              objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
              jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
      
              return jackson2JsonRedisSerializer;
          }
      
          /**
           * 配置redis缓存管理器,管理注解版的缓存
           */
          @Bean
          public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory,RedisSerializer redisSerializer) {
              RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
              //设置Redis缓存有效期为10分钟
              RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                      .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                      //ttl
                      .entryTtl(Duration.ofHours(2));
              return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration);
          }
      
      }
    3. application.yml

      # 配置redis
        redis:
          host: 115.29.111.155
          port: 6379
          database: 0       #默认使用0号数据库
          timeout: 2000     #设置连接超时时间
          lettuce:
            pool:
              max-idle: 10
    4. service层

      @Service
      public class UserServiceImpl implements UserService {
      
          @Autowired
          private UserMapper userMapper;
      
          @CacheEvict(value = RedisConfig.REDIS_USER_KEY,allEntries = true,condition = "#result==1")
          @Override
          public int deleteByPrimaryKey(Integer id) {
              return userMapper.deleteByPrimaryKey(id);
          }
      
          /**
           * @CachePut: 主要用户向数据库中插入数据,向数据中插入数据的时候,会将返回的int类型,放入redis中缓存,当然是有选择性的
           *      属性:
           *      value:key的一部分,命名空间
           *      key:指定key的名称
           *      unless:满足条件,则不将返回的结果放入redis
           *      condition: 满足条件,则将返回的结果放入redis
           */
      //    @CachePut(value = RedisConfig.REDIS_USER_KEY,key = "'insert-'+#p0.id",unless = "#result==0")
          @CacheEvict(value = RedisConfig.REDIS_USER_KEY,allEntries = true,condition = "#result==1")
          @Override
          public int insert(User record) {
              return userMapper.insert(record);
          }
      
      //    @CachePut(value = RedisConfig.REDIS_USER_KEY,key = "'insert-'+#p0.id",unless = "#result==0")
          @CacheEvict(value = RedisConfig.REDIS_USER_KEY,allEntries = true,condition = "#result==1")
          @Override
          public int insertSelective(User record) {
              return userMapper.insertSelective(record);
          }
      
          @Cacheable(value = RedisConfig.REDIS_USER_KEY,key = "'list'",unless = "#result==null")
          @Override
          public List<User> selectByExample() {
              UserExample example = new UserExample();
              example.setOrderByClause("id desc");
              List<User> users = userMapper.selectByExample(example);
              return users;
          }
      
          /**
           *   查找数据库是否存在与该名称相同的用户,如果存在true else false
           *
           *  我们这样规定,对于key而言,返回boolean的加一级 exist
           *
           */
          @Cacheable(value = RedisConfig.REDIS_USER_KEY , key = "'exist-'+#p0")
          @Override
          public boolean findUserByName(String name) {
              UserExample example = new UserExample();
              example.createCriteria().andNameEqualTo(name);
              List<User> users = userMapper.selectByExample(example);
              if(users.size()==0){
                  return false;
              }
              return true;
          }
      
          /**
           * 通过username获取用户,username是唯一的
           * redis中使用缓存缓存数据。
           *
           * @Cacheable 开启基于注解的缓存功能
           *
           * @Cacheable注解 :先从redis数据库中 按照当前key查找,有没有。如果没有再调用方法返回结果,如果结果不为null将其缓存到数据库中
           *      属性:
           *      value:key的一部分(前缀),主要是指明数据放在那个key范围
           *      key:key的主体,#p0:指明取出第一个参数  #p1:指明取出第二个参数。。。依此类推
           *      unless:结果为true,将当前的数据结果不保存到redis,#result:指明取出数据库中返回的结果
           *      condition 结果如果为true,将当前数据保存到redis
           *  此为生成的key,中文再redis中显示是这样的
           * "tmall_springboot::user-\xe5\xbc\xa0\xe4\xb8\x89"
           */
          @Cacheable(value = RedisConfig.REDIS_USER_KEY, key = "'one-name-'+#p0",  unless = "#result==null")
          @Override
          public User getUserByName(String name) {
      //        System.out.println("走该方法,数据应该缓冲");
              UserExample example = new UserExample();
              example.createCriteria().andNameEqualTo(name);
              List<User> users = userMapper.selectByExample(example);
              if(users.size()==0){
                  return null;
              }
              return users.get(0);
          }
      
          /**
           *   通过userName,password,查询是否有此用户
           */
          @Cacheable(value = RedisConfig.REDIS_USER_KEY,key = "'one-'+#p0.id",unless = "#result==null")
          @Override
          public User findOneByNameAndPassword(User user){
              UserExample example = new UserExample();
              example.createCriteria().andNameEqualTo(user.getName()).andPasswordEqualTo(user.getPassword());
              List<User> users = userMapper.selectByExample(example);
              if(users.size()==1){
                  return users.get(0);
              }
              return null;
          }
      
      
          @Cacheable(value = RedisConfig.REDIS_USER_KEY , key = "'one-'+#p0",unless = "#result==null")
          @Override
          public User selectByPrimaryKey(Integer id) {
              return userMapper.selectByPrimaryKey(id);
          }
      
          /**
           * @CacheEvict:满足条件则移除当前key在redis中的数据
           *      属性:
           *      value: 同理命名空间
           *      key: key名称
           *      condition:满足什么条件缓存到redis中
           *      allEntries: 移除value(命名空间)下,全部缓存
           */
          @CacheEvict(value = RedisConfig.REDIS_USER_KEY,allEntries = true,condition = "#result==1")
          @Override
          public int updateByPrimaryKeySelective(User record) {
              return userMapper.updateByPrimaryKeySelective(record);
          }
      
          @CacheEvict(value = RedisConfig.REDIS_USER_KEY,allEntries = true,condition = "#result==1")
          @Override
          public int updateByPrimaryKey(User record) {
              return userMapper.updateByPrimaryKey(record);
          }
      }
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
24天前
|
缓存 NoSQL Java
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解分布式情况下如何添加分布式锁 【续篇】
这篇文章是关于如何在SpringBoot应用中整合Redis并处理分布式场景下的缓存问题,包括缓存穿透、缓存雪崩和缓存击穿。文章详细讨论了在分布式情况下如何添加分布式锁来解决缓存击穿问题,提供了加锁和解锁的实现过程,并展示了使用JMeter进行压力测试来验证锁机制有效性的方法。
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解分布式情况下如何添加分布式锁 【续篇】
|
13天前
|
编解码 NoSQL Java
使用Spring Boot + Redis 队列实现视频文件上传及FFmpeg转码的技术分享
【8月更文挑战第30天】在当前的互联网应用中,视频内容的处理与分发已成为不可或缺的一部分。对于视频平台而言,高效、稳定地处理用户上传的视频文件,并对其进行转码以适应不同设备的播放需求,是提升用户体验的关键。本文将围绕使用Spring Boot结合Redis队列技术来实现视频文件上传及FFmpeg转码的过程,分享一系列技术干货。
49 3
|
17天前
|
Java 开发者 Spring
【SpringBoot 异步魔法】@Async 注解:揭秘 SpringBoot 中异步方法的终极奥秘!
【8月更文挑战第25天】异步编程对于提升软件应用的性能至关重要,尤其是在高并发环境下。Spring Boot 通过 `@Async` 注解简化了异步方法的实现。本文详细介绍了 `@Async` 的基本用法及配置步骤,并提供了示例代码展示如何在 Spring Boot 项目中创建与管理异步任务,包括自定义线程池、使用 `CompletableFuture` 处理结果及异常情况,帮助开发者更好地理解和运用这一关键特性。
83 1
|
14天前
|
缓存 Java 数据库连接
Spring Boot奇迹时刻:@PostConstruct注解如何成为应用初始化的关键先生?
【8月更文挑战第29天】作为一名Java开发工程师,我一直对Spring Boot的便捷性和灵活性着迷。本文将深入探讨@PostConstruct注解在Spring Boot中的应用场景,展示其在资源加载、数据初始化及第三方库初始化等方面的作用。
42 0
|
21天前
|
Web App开发 前端开发 关系型数据库
基于SpringBoot+Vue+Redis+Mybatis的商城购物系统 【系统实现+系统源码+答辩PPT】
这篇文章介绍了一个基于SpringBoot+Vue+Redis+Mybatis技术栈开发的商城购物系统,包括系统功能、页面展示、前后端项目结构和核心代码,以及如何获取系统源码和答辩PPT的方法。
|
14天前
|
缓存 NoSQL Java
惊!Spring Boot遇上Redis,竟开启了一场缓存实战的革命!
【8月更文挑战第29天】在互联网时代,数据的高速读写至关重要。Spring Boot凭借简洁高效的特点广受开发者喜爱,而Redis作为高性能内存数据库,在缓存和消息队列领域表现出色。本文通过电商平台商品推荐系统的实战案例,详细介绍如何在Spring Boot项目中整合Redis,提升系统响应速度和用户体验。
40 0
|
14天前
|
监控 安全 Java
【开发者必备】Spring Boot中自定义注解与处理器的神奇魔力:一键解锁代码新高度!
【8月更文挑战第29天】本文介绍如何在Spring Boot中利用自定义注解与处理器增强应用功能。通过定义如`@CustomProcessor`注解并结合`BeanPostProcessor`实现特定逻辑处理,如业务逻辑封装、配置管理及元数据分析等,从而提升代码整洁度与可维护性。文章详细展示了从注解定义、处理器编写到实际应用的具体步骤,并提供了实战案例,帮助开发者更好地理解和运用这一强大特性,以实现代码的高效组织与优化。
28 0
|
18天前
|
缓存 NoSQL Java
【Azure Redis 缓存】定位Java Spring Boot 使用 Jedis 或 Lettuce 无法连接到 Redis的网络连通性步骤
【Azure Redis 缓存】定位Java Spring Boot 使用 Jedis 或 Lettuce 无法连接到 Redis的网络连通性步骤
|
18天前
|
缓存 NoSQL 网络安全
【Azure Redis 缓存】Redis连接无法建立问题的排查(注:Azure Redis集成在VNET中)
【Azure Redis 缓存】Redis连接无法建立问题的排查(注:Azure Redis集成在VNET中)
|
20天前
|
缓存 NoSQL 网络协议
【Azure Redis 缓存 Azure Cache For Redis】在创建高级层Redis(P1)集成虚拟网络(VNET)后,如何测试VNET中资源如何成功访问及配置白名单的效果
【Azure Redis 缓存 Azure Cache For Redis】在创建高级层Redis(P1)集成虚拟网络(VNET)后,如何测试VNET中资源如何成功访问及配置白名单的效果