搜尋

首頁  >  問答  >  主體

springboot - spring boot redis 保存hash物件使用自訂id問題

  1. 使用的spring boot 相關情況

目前使用的spring boot 的版本是1.5.2.RELEASE, 資料庫操作使用的是spring-boot-starter-data-jpa,redis使用的是spring-boot-starter-data-redis

資料庫操作我使用的是spring boot 提供的 JPA repository,redis使用的是Redis Repositories。

一個常見的場景是,透過 JPA 將一個資料保存到 mysql 中,成功之後,更新 redis 的 hash物件。

按照 spring data redis repositories 官方文件的介紹我需要配置下實體。

2.相關程式碼

以保存訂單為例,傳入一個DTO,呼叫jpa方法寫入到資料庫,成功之後寫入快取。
主要的程式碼如下:

主檔案中使用啟用快取功能
@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重寫了一些快取的功能

@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 呼叫保存方法保存資料到資料庫,這裡使用了@CachePut註解,產生的key的主鍵是

order:100

這種形式

    @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();
        }
    }

實體的設定如下,這裡同時使用了 JPA 的配置和 Redis的配置,就是這塊兒比較模糊,不知道是否正確。

@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;
    }
}

我現在遇到的主要問題是: 1) 在Order實體配置中,如果我在Id上配置了redis 的ID 註解@org.springframework.data.annotation.Id , 產生的redis key類似這樣order:1222702657038933405, 我想要的效果是產生的key直接使用訂單id,類似這樣

order:100

. 我在Service上有設定

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

但沒有生效。

2) debug的時候發現,就算生成的對象,id也有了仍然會報表達式的id屬性不存在

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) 拋開redis建立的key不對的問題,發現redis物件能夠保存成功,但是資料庫的記錄確沒有成功建立。如果去掉Order實體上的

@RedisHash(value = "order")###註解,資料庫記錄能夠成功創建,但是快取又無法創建成功了。 ###
阿神阿神2748 天前1762

全部回覆(1)我來回復

  • 给我你的怀抱

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

    首先key應該是屬性名稱

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

    其次@RedisHash在物件持久化到redis中使用,你這裡要使用的是redis緩存,不是持久化,所以不關RedisHash什麼事。

    回覆
    0
  • 取消回覆