前言
在使用 Redis 缓存时,Java 8 中的日期类序列化会报错。有以下两种解决方法:
Redis 配置类中添加对应序列化/反序列化器
@Slf4j
@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
//过期时间 1 小时
private Duration timeToLive = Duration.ofHours(1L);
/**
* RedisTemplate 相关配置
*
* @param factory
* @return
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(factory);
// key采用String的序列化方式
template.setKeySerializer(keySerializer());
// hash的key也采用String的序列化方式
template.setHashKeySerializer(keySerializer());
// value序列化方式采用jackson
template.setValueSerializer(valueSerializer());
// hash的value序列化方式采用jackson
template.setHashValueSerializer(valueSerializer());
template.afterPropertiesSet();
return template;
}
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
//配置 key value 序列化器,过期时间
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(timeToLive)
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer()))
.disableCachingNullValues();
RedisCacheManager redisCacheManager = RedisCacheManager.builder(connectionFactory)
.cacheDefaults(config)
.transactionAware()
.build();
return redisCacheManager;
}
/**
* key 序列化
* @return
*/
private RedisSerializer<String> keySerializer() {
return new StringRedisSerializer();
}
/**
* 值采用JSON序列化
* @return
*/
private RedisSerializer<Object> valueSerializer() {
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
//LocalDate、LocalDateTime序列化器
JavaTimeModule timeModule = new JavaTimeModule();
timeModule.addDeserializer(LocalDate.class,
new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
timeModule.addDeserializer(LocalDateTime.class,
new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
timeModule.addSerializer(LocalDate.class,
new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
timeModule.addSerializer(LocalDateTime.class,
new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
om.registerModule(timeModule);
jackson2JsonRedisSerializer.setObjectMapper(om);
return jackson2JsonRedisSerializer;
}
/**
* 处理缓存异常,不能影响正常业务
* @return
*/
@Override
public CacheErrorHandler errorHandler() {
return new CacheErrorHandler() {
@Override
public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {
handleRedisCacheErrorException(exception, key);
}
@Override
public void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) {
handleRedisCacheErrorException(exception, key);
}
@Override
public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) {
handleRedisCacheErrorException(exception, key);
}
@Override
public void handleCacheClearError(RuntimeException exception, Cache cache) {
handleRedisCacheErrorException(exception, null);
}
};
}
protected void handleRedisCacheErrorException(RuntimeException exception, Object key) {
log.error("redis exception :key=[{}]", key, exception);
}
}
字段上增加注解
实体类如下:在字段上添加 @JsonDeserialize(using = LocalDateDeserializer.class)
和 @JsonSerialize(using = LocalDateSerializer.class)
注解来指定序列化和反序列化器。
实体类
/**
* @author Leo
* @create 2020-07-17 10:36
**/
@Data
@Accessors(chain = true)
public class Book implements Serializable {
private static final long serialVersionUID = -444903545130170434L;
private String id;
private String name;
private String summary;
private Integer price;
// @JsonDeserialize(using = LocalDateDeserializer.class)
// @JsonSerialize(using = LocalDateSerializer.class)
// @JsonFormat( pattern="yyyy-MM-dd")
private LocalDate publishDate;
}
Service 类
/**
* @author Leo
* @create 2020-07-17 10:36
**/
@Slf4j
@Service
public class BookService {
private static List<Book> books = new ArrayList<>();
static {
for(int i = 0; i < 20; i++) {
books.add(new Book()
.setId(String.valueOf(i))
.setName("Book" + i)
.setPrice(new Random().nextInt(200))
.setPublishDate(LocalDate.now()));
}
}
@Cacheable(value = "books", key = "#root.methodName")
public List<Book> selectAll() {
log.info("selectAll from DB");
return books;
}
@Cacheable(value = "books", key = "#id")
public Book selectById(String id) {
log.info("selectById from DB, id: " + id);
return new Book().setId("999").setName("default").setPrice(100).setPublishDate(LocalDate.now());
}
@CacheEvict(value = "books", key = "#book.id")
public void update(Book book) {
log.info("update : " + book.toString());
}
@CacheEvict(value = "books", allEntries = true)
public void delete(String id) {
log.info("delete : " + id);
}
}
测试
/**
* @author Leo
* @program redis-demo
* @description
* @create 2020-07-17 22:36
**/
@Slf4j
@SpringBootTest(classes = RedisDemoApplication.class)
@RunWith(SpringRunner.class)
public class RedisDemoApplicationTests {
@Resource
private BookService bookService;
@Test
public void testSelectAll() {
log.info("first selectAll");
bookService.selectAll();
log.info("second selectAll");
bookService.selectAll();
}
@Test
public void testSelectById() {
//多次查询统一个id的数据
bookService.selectById("0");
bookService.selectById("0");
}
@Test
public void testUpdate() {
bookService.update(new Book().setId("0"));
// bookService.selectAll();
}
@Test
public void testDelete() {
bookService.delete("0");
// bookService.selectAll();
}
}