search

Home  >  Q&A  >  body text

springboot - spring boot redis save hash object using custom id problem

  1. Related information about spring boot used

The version of spring boot currently used is 1.5.2.RELEASE, the database operation uses spring-boot-starter-data-jpa, and the redis uses spring-boot-starter-data-redis

For database operations, I use the JPA repository provided by spring boot, and redis uses Redis Repositories.

A common scenario is to save a data to mysql through JPA, and after success, update the hash object of redis.

According to the introduction of the spring data redis repositories official document, I need to configure the entity.

2.Related code

Take saving an order as an example, pass in a DTO, call the jpa method to write to the database, and write to the cache after success.
The main code is as follows:

Enable caching function in the main file
@EnableRedisRepositories(basePackages = {"com.test"})
@EnableCaching

@SpringBootApplication
@EnableJpaRepositories(basePackages = {"com.test"})
@EnableRedisRepositories(basePackages = {"com.test"})
@EnableCaching
@EnableJpaAuditing
@EntityScan(basePackages = {"com.test"})
@ComponentScan(basePackages = {"com.test"} )
public class Application {

    private static final Logger LOGGER = LoggerFactory.getLogger(Application.class);

    public static void main(String[] args) {
        final ApplicationContext applicationContext = SpringApplication.run(Application.class, args);
        final String[] activeProfiles = applicationContext.getEnvironment().getActiveProfiles();
        for (String profile : activeProfiles) {
            LOGGER.info("using profile {}", profile);
        }
    }
}

RedisConfig rewrites some caching functions

@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {

    @Override
    @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());
                }
                System.out.println(sb.toString());
                return sb.toString();
            }
        };
    }

    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
        StringRedisTemplate template = new StringRedisTemplate(factory);
        Jackson2JsonRedisSerializer 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);
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }

    @Bean
    public CacheManager cacheManager(RedisTemplate redisTemplate) {
        RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
        cacheManager.setDefaultExpiration(300);
        return cacheManager;
    }
}

OrderService calls the save method to save the data to the database. The @CachePut annotation is used here. The primary key of the generated key is order:100 in this form

    @CachePut(value = "order", key = "#order.id")
    @Override
    public void save(OrderDTO orderDTO) {
        try {
            Order order = BeanMapper.map(orderDTO, Order.class);
            order = orderRepository.save(order);
            System.out.println(order);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

The settings of the entities are as follows. Both JPA configuration and Redis configuration are used here. This is a bit vague and I don’t know if it is correct.

@Entity
@Table(name = "order")
@RedisHash(value = "order")
public class Order {
    private Long id;
    private Long userId;

    @org.springframework.data.annotation.Id
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    public Long getId() {
        return id;
    }

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

The main problem I am encountering now is:

1) In the Order entity configuration, if I configure the redis ID annotation @org.springframework.data.annotation.Id on Id , the generated redis key is similar to this order:1222702657038933405, the effect I want is that the generated key directly uses the order id, similar to this order:100.

I have configuration on Service @CachePut(value = "order", key = "#order.id") but it does not take effect.

2) When debugging, I found that even if the generated object has an id, it will still report that the id attribute of the expression does not exist

Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1007E: Property or field 'id' cannot be found on null
    at org.springframework.expression.spel.ast.PropertyOrFieldReference.readProperty(PropertyOrFieldReference.java:220) ~[spring-expression-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.java:94) ~[spring-expression-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.expression.spel.ast.PropertyOrFieldReference.accessrrreee0(PropertyOrFieldReference.java:46) ~[spring-expression-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.expression.spel.ast.PropertyOrFieldReference$AccessorLValue.getValue(PropertyOrFieldReference.java:375) ~[spring-expression-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:88) ~[spring-expression-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.expression.spel.ast.SpelNodeImpl.getValue(SpelNodeImpl.java:120) ~[spring-expression-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:242) ~[spring-expression-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.cache.interceptor.CacheOperationExpressionEvaluator.key(CacheOperationExpressionEvaluator.java:117) ~[spring-context-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.cache.interceptor.CacheAspectSupport$CacheOperationContext.generateKey(CacheAspectSupport.java:742) ~[spring-context-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.cache.interceptor.CacheAspectSupport.generateKey(CacheAspectSupport.java:558) ~[spring-context-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.cache.interceptor.CacheAspectSupport.collectPutRequests(CacheAspectSupport.java:529) ~[spring-context-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:413) ~[spring-context-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:327) ~[spring-context-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:61) ~[spring-context-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:656) ~[spring-aop-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at com.jiabangou.order.services.impls.OrderServiceImpl$$EnhancerBySpringCGLIB$ceab7f.save(<generated>) ~[classes/:na]
    at com.jiabangou.bops.controllers.api.OrderApiController.save(OrderApiController.java:32) ~[classes/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_65]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_65]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_65]
    at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_65]
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) ~[spring-web-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133) ~[spring-web-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:116) ~[spring-webmvc-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827) ~[spring-webmvc-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738) ~[spring-webmvc-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) ~[spring-webmvc-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963) ~[spring-webmvc-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897) ~[spring-webmvc-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) ~[spring-webmvc-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    ... 95 common frames omitted

3) Putting aside the problem that the key created by redis is incorrect, we found that the redis object can be saved successfully, but the database record is not successfully created. If you remove the @RedisHash(value = "order") annotation on the Order entity, the database record can be created successfully, but the cache cannot be created successfully.

阿神阿神2748 days ago1760

reply all(1)I'll reply

  • 给我你的怀抱

    给我你的怀抱2017-05-16 13:22:12

    First of all, the key should be the attribute name

    @CachePut(value = "order", key = "#orderDTO.id")
    

    Secondly, @RedisHash is used to persist objects to redis. What you want to use here is redis cache, not persistence, so it has nothing to do with RedisHash.

    reply
    0
  • Cancelreply