1. Introduction

The previous article explained how to set the cache using SpringCache and Redis, but the SpringCache annotation does not support setting the cache time, which is a real headache. This article will show you the easiest way to set SpringCache and Redis cache and set cache times. This post is based on the previous blog, so if you don’t know anything, check out the previous blog. Link to previous article: Elegant Caching Solution –SpringCache and Redis Integration (SpringBoot)

2. The configuration

The @cacheable annotation does not support expiration, so you need to configure the default expiration and cache expiration for each class or method by configuring CacheManneg.

Solution You can use the following configuration information to solve the problem of setting the expiration time

Modify configuration class

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
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.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import java.io.Serializable;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

/ * * *@Author: MaoLin
 * @Date: 2019/3/26 17:04
 * @Version1.0 * /

@Configuration
@EnableCaching
public class RedisConfig implements Serializable {

     /** * Declare the cache manager, creating an aspect and triggering a Pointcut for Spring cache annotations * Depending on the annotations used by the class or method and the state of the cache, the aspect will fetch data from the cache, add data to the cache or remove a value from the cache */

   /* @Bean public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) { return RedisCacheManager.create(redisConnectionFactory); } @bean public RedisTemplate RedisTemplate (RedisConnectionFactory Factory) {// create a template class RedisTemplate
      
        template = new RedisTemplate
       
        (); / / will just redis connection factory Settings to the template class template. SetConnectionFactory (factory); / / set the key of the serializer template. SetKeySerializer (new StringRedisSerializer ()); // Set the serializer for value // Use Jackson 2, The Object serialization to JSON Jackson2JsonRedisSerializer Jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer (Object. The class); ObjectMapper om = new ObjectMapper(); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); template.setValueSerializer(jackson2JsonRedisSerializer); return template; } * /
       ,>
      ,>


    /** * Set the redis cache expiration time */

    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        return new RedisCacheManager(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory), this.getRedisCacheConfigurationWithTtl( 60), this.getRedisCacheConfigurationMap() // Specify the key policy
        );
    }

    private Map<String, RedisCacheConfiguration> getRedisCacheConfigurationMap(a) {
        Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();
        // Configure the expiration time for the SsoCache and BasicDataCache
     redisCacheConfigurationMap.put("messagCache".this.getRedisCacheConfigurationWithTtl(30 * 60));   redisCacheConfigurationMap.put("userCache".this.getRedisCacheConfigurationWithTtl(60));// Customize the cache time
    
        return redisCacheConfigurationMap;
    }

    private RedisCacheConfiguration getRedisCacheConfigurationWithTtl(Integer seconds) {
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
        redisCacheConfiguration = redisCacheConfiguration.serializeValuesWith(
                RedisSerializationContext
                        .SerializationPair
                        .fromSerializer(jackson2JsonRedisSerializer)
        ).entryTtl(Duration.ofSeconds(seconds));

        returnredisCacheConfiguration; }}Copy the code

test

  • Set the cache name and cache time (as follows: 60 seconds)
redisCacheConfigurationMap.put("userCache".this.getRedisCacheConfigurationWithTtl(60));
Copy the code
  • Use annotations@Cacheable("userCache") Note: The name is userCache set in the configuration class. Multiple cache names and time can be set

The Controller test class


import com.ml.demo.dao.UserDao;
import com.ml.demo.entity.User;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.io.Serializable;

/ * * *@Author: MaoLin
 * @Date: 2019/3/26 17:03
 * @Version1.0 * /


@RestController
public class testController implements Serializable {
    @Resource
    private UserDao userDao;

    /** * select * from cache **@param userId
     * @return* /
    @RequestMapping("/getUser")
    @Cacheable("userCache")
    public User getUser(@RequestParam(required = true) String userId) {
        System.out.println("If there is no cache, the following method is called, if there is a cache, it simply prints, it does not print this paragraph.");
        return userDao.getUser(Integer.parseInt(userId));
    }

    /** * delete a cache **@param userId
     * @return* /
    @RequestMapping(value = "/deleteUser")
    @CacheEvict("userCache")
    public String deleteUser(@RequestParam(required = true) String userId) {
        return "Deletion successful";
    }

    /** * add a saved data to the cache. The cache key is the current user id **@param user
     * @return* /
    @RequestMapping("/saveUser")
    @CachePut(value = "userCache", key = "#result.userId +''")
    public User saveUser(User user) {
        return user;
    }


    /** * Return the result userPassword contains nocache string not cache **@param userId
     * @return* /
    @RequestMapping("/getUser2")
    @CachePut(value = "userCache", unless = "#result.userPassword.contains('nocache')")
    public User getUser2(@RequestParam(required = true) String userId) {
        System.out.println("If you go here, the cache is not in effect!");
        User user = new User(Integer.parseInt(userId), "name_nocache" + userId, "nocache");
        return user;
    }


    @RequestMapping("/getUser3")
    @Cacheable(value = "userCache", key = "#root.targetClass.getName() + #root.methodName + #userId")
    public User getUser3(@RequestParam(required = true) String userId) {
        System.out.println("If I don't get there the second time, the cache has been added.");
        returnuserDao.getUser(Integer.parseInt(userId)); }}Copy the code

Test runs and results

  • Save the cache

  • Check the cache

  • Check the redis

  • The cache expires one minute later

  • Query cache again

  • Console running result

3. The error is resolved

[[.[/].[/].[dispatcherServlet] : servlet.service ()for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.data.redis.serializer.SerializationException: Could not read JSON: Cannot construct instance of `com.ml.demo.entity.User` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
 at [Source: (byte[])"["com.ml.demo.entity.User"{"userId": 11,"userName":"\"Zhang SAN \" "."userPassword":"123"}]"; line: 1, column: 29]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.ml.demo.entity.User` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator) at [Source: (byte[])"["com.ml.demo.entity.User", {"userId": 11."userName":"\" zhang SAN \ ""."userPassword":"123"}]"; line: 1, column: 29]] with root cause
Copy the code

This bug took a long time to fix, but it’s actually quite simple.

The reason:

The reason is that I added a constructor to the entity class to make it easier to instantiate the class. As a result, the JVM will not add the default no-argument constructor, and Jackson’s deserialization requires no-argument constructors, so an error is reported.

The same goes for the Response entity class.

Solution:

Just add a no-parameter constructor to the entity class.

public User() {}

Summary & References

summary

Using Spring’s caching mechanism (objects) in conjunction with Redis is actually a good way to implement caching, but it does not provide caching time, which is very impersonal. Spring also provides RedisTemplate and StringRedisTemplate classes, both of which support caching times. If SpringCache is not easy to use, you will need to set the cache time. You can use the RedisTemplate class to customize the Redis utility class to implement caching.

  • Git source code, clone and fork welcome

The resources

  • When redis and cache are integrated with Spring 2.0 or above, @cacheable time expires
  • Spring the Boot during deserialization: Jackson. Databind. Exc. InvalidDefinitionException always deserialize the from Object of value