Elasticsearch 8.13.4 LocalDateTime类型转换问题
框架背景
springboot 3.3.1+elasticseach8.13.4+spring-data-elasticsearch5.3.1(其实只要用了springboot3.3.1 上下两个的版本都在里面绑死了)
问题描述
使用spring-data-elasticsearch操作es,当字段增加映射注解,其实如果是日期类型,你不加默认也给你映射成date了
@Field(type = FieldType.Date)
可以正常保存成功,但查询时会报错
org.springframework.data.elasticsearch.core.convert.ConversionException: Unable to convert value ‘2024-08-30’ to java.time.LocalDateTime for property ‘createTime’
通过kibana查看数据
发现保存的数据格式是 “2024-08-30”,导致读取时解析失败
解决方案使用自定义的转换器,我这里是将LocalDateTime保存为时间戳,读取的时候再转为LocalDateTime
以下是配置类(我是自定义了一个starer,所以用了@AutoConfiguration)
package cn.iocoder.centralstore.framework.es.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.data.elasticsearch.core.mapping.PropertyValueConverter;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
/***
* @Description: 处理 Elasticsearch 中 LocalDateTime 与时间戳之间的转换
* 将 LocalDateTime 写入为时间戳,读取时将时间戳转换为 LocalDateTime。
* @Author: TaoYuan
* @Date: 2024/8/29
*/
@AutoConfiguration
@Slf4j
public class CentralstoreLocalDateTimeConverter implements PropertyValueConverter {
// 使用系统默认时区
private static final ZoneId ZONE_ID = ZoneId.systemDefault();
@Override
public Object write(Object value) {
if (value instanceof LocalDateTime localDateTime) {
Instant instant = localDateTime.atZone(ZONE_ID).toInstant();
long timestamp = instant.toEpochMilli();
log.info("将 LocalDateTime [{}] 转换为时间戳 [{}]", localDateTime, timestamp);
return timestamp;
} else {
String errorMessage = String.format("写入操作接收到非 LocalDateTime 值: [%s], 类型: [%s]", value, value.getClass().getName());
log.error(errorMessage);
throw new IllegalStateException(errorMessage);
}
}
@Override
public Object read(Object value) {
if (value instanceof Long timestamp) {
LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZONE_ID);
log.info("将时间戳 [{}] 转换为 LocalDateTime [{}]", timestamp, localDateTime);
return localDateTime;
} else {
String errorMessage = String.format("无法将值 '值: [%s], 类型: [%s] 解析为 LocalDateTime", value,value.getClass().getName());
log.error(errorMessage);
throw new IllegalStateException(errorMessage);
}
}
}
字段增加注解
@Field(type = FieldType.Date)
@ValueConverter(CentralstoreLocalDateTimeConverter.class)
private LocalDateTime createTime;```
至此,插入数据和查询数据LocalDateTime类型就搞定了。其实里面还有很多细节,但是大部分估计跟我一样,只想着找到解决方案,不去想为什么会这样。所以就懒得继续深讲了。
还有很多坑,比如使用雪花算法生成的Id是19位,存入es后后两位精度丢失,这个处理起来最简单的办法就是使用String类型的作为id。或者跟上面一样 增加一个Long类型的转换器,转为String ,读取的时候再转换为Long。这个我还没有尝试,只是目前改了一下id的数据类型。
到现在为止仍然没有整合完,还有一堆坑等着我,次奥!!!次奥!!!次奥!!!