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.
First of all, the key should be the attribute name
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.