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();@Overridepublic 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);}}@Overridepublic 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的数据类型。
到现在为止仍然没有整合完,还有一堆坑等着我,次奥!!!次奥!!!次奥!!!