Spring Cache + Redis implements caching

Posted by chriskiely on Sun, 30 Jan 2022 01:04:37 +0100

Spring Boot cache

Several important concepts & cache annotation

CacheCache interface, which defines cache operations. Implementations include RedisCache, EhCacheCache, ConcurrentMapCache, etc
CacheManagerThe Cache manager manages various Cache components
@CacheableIt is mainly for method configuration, and the results can be cached according to the request parameters of the method
@CacheEvictwipe cache
@CachePutEnsure that the method is called and that the result is cached
@EnableCachingEnable annotation based caching
keyGeneratorkey generation strategy when caching data
serializevalue serialization policy when caching data

Spring Boot cache annotation introduction

  • @EnableCaching annotation
  • @Cacheable annotation
  • @CachePut annotation
  • @CacheEvict annotation
  • @Caching annotation
  • @CacheConfig annotation

@EnableCaching

It is provided by the Spring framework. The Spring Boot framework inherits the annotation. The annotation needs to be configured on the class (usually on the project startup class in Spring Boot) to enable annotation based caching support

@Cacheable annotation

@Cacheable annotation is also provided by the Spring framework. It can act on classes or methods (usually used on data query methods) to cache and store method results.

@The execution order of Cacheable annotation is to query the cache first. If it is empty, query the method and cache the results; If there is data in the cache, the method query is not performed, but the cached data is used directly.

@Cacheable annotation (attribute)

Attribute nameexplain
value/cacheNamesSpecifies the name of the cache space. Required attribute. Use one of these two attributes
keySpecify the key of the cached data. By default, the method parameter value is used. You can use the SpEL expression
keyGeneratorSpecifies the generator of the key for caching data, which can be used with one of the key attributes
cacheManagerSpecify cache manager
cacheResolverSpecifies the cache parser, which can be used with one of the cacheManager properties
conditionSpecifies data caching when certain conditions are met
unlessSpecifies that data caching will not be performed under certain conditions
syncSpecifies whether to use asynchronous caching. Default false

@Introduction to CachePut annotation

@CachePut annotation is provided by the Spring framework and can act on classes or methods (usually used on data update methods). The function of this annotation is to update cached data.

@The execution order of CachePut annotation is to call the method first, and then update the method result to the cache.

@The CachePut annotation also provides multiple attributes, which are exactly the same as those of the @ Cacheable annotation

@Introduction to CacheEvict annotation

@CacheEvict annotation is provided by the Spring framework and can act on classes or methods (usually used on data deletion methods). The function of this annotation is to delete cached data.

@The default execution order of CacheEvict annotation is to make method calls first, and then clear the cache.

@CacheEvict annotation also provides multiple attributes, which are basically the same as those of @ Cacheable annotation. In addition, it also provides two special attributes allEntries and beforeInvocation

@CacheEvict annotation (attribute)

(1) allEntries property
The allEntries property indicates whether to clear all cached data in the specified cache space. The default value is false (that is, only the cached data corresponding to the specified key is deleted by default).
(2) beforeInvocation property
The beforeInvocation property indicates whether the cache is cleared before the method is executed. The default value is false (that is, the cache is cleared after the method is executed by default)

@Introduction to Caching annotation

@Caching annotation is used for data cache management of complex rules and can act on classes or methods. The @ caching annotation contains three attributes: Cacheable, put and evict, which correspond to @ Cacheable, @ CachePut and @ CacheEvict annotations respectively

@Caching annotation usage (sample code)

@Caching(cacheable={@Cacheable(cacheNames ="comment",key = "#id")},
	put = {@CachePut(cacheNames = "comment",key = "#result.author")})
	public Comment getComment(int comment_id){
	return commentRepository.findById(comment_id).get();
}

Spring Cache + Redis implements caching

Spring Cache is an excellent caching component. Since Spring 3.1, annotation Cache support similar to @ Transactional annotation transactions is provided, and Cache abstraction is provided to facilitate switching between various underlying caches (such as redis)

@EnableCaching: Mark @ EnableCaching, enable caching, and configure Redis cache manager.

@The EnableCaching annotation triggers the post processor to check whether there are cache annotations in the public method of each Spring bean. If such a comment is found, a proxy is automatically created to intercept method calls and handle the corresponding cache behavior

1. Required dependencies

<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!-- spring2.X integrate redis what is needed common-pool2-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.6.0</version>
</dependency>

2. Configuration file

#redis configuration
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.database= 0
spring.redis.timeout=1800000
#redis connection pool configuration
spring.redis.lettuce.pool.max-active=20
spring.redis.lettuce.pool.max-wait=-1
#Maximum blocking waiting time (negative number means no limit)
spring.redis.lettuce.pool.max-idle=5
spring.redis.lettuce.pool.min-idle=0

3. redis configuration class

@Configuration
@EnableCaching
public class RedisConfig {


    /**
     * Custom key rule
     * @return
     */
    @Bean
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getName());
                sb.append(method.getName());
                for (Object obj : params) {
                    sb.append(obj.toString());
                }
                return sb.toString();
            }
        };
    }

    /**
     * Set RedisTemplate rules
     * @param redisConnectionFactory
     * @return
     */
    /**
     * Define your own redisTemplate object. If there is a redisTemplate object in the container,
     * springboot The default redisTemplate object will not be injected, and the name attribute may not be specified. The method name must be redisTemplate
     * (The default name of the bean should be the method name. This mechanism can also exclude the spring boot injection (default redisTemplate)
     */
    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

        //(take out the data serialization configuration)
        // Configure the problem that the json string cannot be converted into a java Bean when the template object is used to read the json string from the cache. If it is not configured, an exception will be thrown
        // (java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to
        // com.springboot.enty.Stu)
        //Solve the problem of abnormal query cache conversion
        ObjectMapper om = new ObjectMapper();
        // Specify the fields to be serialized, field,get and set, and modifier range. ANY includes private and public
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // Specify the type of serialized input. The class must be non final modified. Final modified classes, such as string and integer, will run exceptions
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);

        //Serial number key value
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);

        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    /**
     * Set CacheManager cache rules
     * @param factory
     * @return
     */
    /**
     * Customize the cache manager and inject RedisCacheManager (not provided by springboot by default),
     * The following cache manager configures the cache expiration time (if there are other requirements, you need to redefine the cache manager and specify the corresponding cache manager when using cache annotation)
     * The expiration time is only valid for those annotations of the Cache, such as (@ Cacheable, @ CachePut), which has nothing to do with the Cache added by the redisTemplate object
     * And the serialization setting of cache annotation access data
     */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

        //(take out the data serialization configuration)
        // The configuration uses @ Cacheable(value="'stus'",key="'stu:1 '") annotation when reading from the cache for the second time
        // The json string cannot be converted into a java Bean when the data is not configured. If it is not configured, an exception will be thrown
        // (java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to
        // com.springboot.enty.Stu)
        //Solve the problem of abnormal query cache conversion
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);

        // Configure serialization (solve the problem of garbled code), and the expiration time is 600 seconds
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(600))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))           .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                .disableCachingNullValues();

        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .build();
        return cacheManager;
    }


}

4. List two interfaces

    /**
     * Query sub node (including data dictionary)
     * Use redis+spring cache to cache the data dictionary
     * @param id
     * @return
     */
    @Cacheable(value = "dict",keyGenerator = "keyGenerator")
    @Override
    public List<Dict> findChlidData(Long id) {
        //1. Query by parent id
        List<Dict> dictList = baseMapper.selectList(new QueryWrapper<Dict>().eq("parent_id", id));
        //2. Set hasChildren to each dict object in the list collection
        for (Dict dict : dictList) {
            Long dictId = dict.getId();
            //2.1. Judge whether there is child node data
            boolean hasChildren = hasChildren(dictId);
            dict.setHasChildren(hasChildren);
        }
        return dictList;
    }
     /**
     * Import data dictionary
     * Clear redis cache when importing data dictionary
     * @param file
     */
    @CacheEvict(value = "dict",allEntries = true)
    @Override
    public void importData(MultipartFile file) {
        try {
            EasyExcel.read(file.getInputStream(),DictEeVo.class,new DictListener(baseMapper)).sheet().doRead();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

Topics: Redis Spring Boot Cache