我最近更新到了spring data elasticsearch
的最新版本。当我试图保存或读取数据时,我总是会收到以下错误,
无法转换属性的值"2022-01-30T20:44:43.786438"'创建时间'引起原因:java.time.temporal.UnsupportedTemporalTypeException:不支持的字段:OffsetSeconds
以前在旧版本中,我使用以下
@Field(type = FieldType.Date, format = DateFormat.custom, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ")
LocalDateTime createdTime;
所以我现在尝试了以下方法。但在将数据保存到弹性搜索时,它再次抛出了相同的上述错误。用这种方式读取数据很好。
@Field(type = FieldType.Date, format = {}, pattern = "uuuu-MM-dd'T'HH:mm:ss.SSSX")
LocalDateTime createdTime;
如何解决此问题?为什么它在读取数据时可以正常工作,而在保存时却不能正常工作?此外,我还有以这种日期时间格式保存的旧数据。因此,我需要一个解决方案来解决这个问题,以克服这个错误,并使其与我现有的旧数据兼容。提前谢谢。
LocalDateTime
没有时区。但是您希望使用一个具有时区(X
(的模式来转换它。因此,您会得到一个错误,即时态对象不包含偏移(时区(信息。
您应该将定义更改为:
@Field(type = FieldType.Date, pattern = "uuuu-MM-dd'T'HH:mm:ss.SSS", format = {})
如果您需要时区,也可以使用ZoneDateTime
。
这在以前的版本中起作用的原因是,我们可以使用Elasticsearch核心库中的一些类,这些类进行了大量的自定义处理和回退。我们现在不能再使用这些了,所以我们使用JDK中的标准DateTimeFormatter
和定义的模式,这有时不太灵活。
编辑31.01.2022:
正如评论中所说,Elasticsearch返回的字符串不仅不包含区域信息,还有存在区域信息的情况。
因此,当您需要准备一个没有区域信息的值时,您将需要一个LocalDateTime
,当有可用的区域信息时,您需要一个ZonedDateTime
。没有现成的解决方案,只使用@Field
注释参数,但正如您所写的,您使用的是最新版本的Spring Data Elasticsearch,应该是4.3。由于这个版本,您可以为单个属性定义自定义转换器(而不是为整个类只定义一次(。
让我展示一下如何做到这一点:
我将createdTime
属性的类更改为ZonedDateTime
,当读取没有区域信息的数据时,我会将其设置为UTC。还可以保留LocalDateTime
并从最终读取的值中去除时区。
重要的是附加注释:
@Field(type = FieldType.Date, pattern = "uuuu-MM-dd'T'HH:mm:ss.SSSX||uuuu-MM-dd'T'HH:mm:ss.SSS", format = {})
@ValueConverter(CustomZonedDateTimeConverter.class)
private ZonedDateTime someDate;
保留@Field
注释的原因是在应用程序创建索引时编写正确的映射。
@ValueConverter
注释导致创建给定类的转换器(必须具有无参数构造函数(并将其附加到此属性。它不会用于应用程序中的任何其他ZonedDateTime
属性,这使它不同于标准的Spring数据转换器。实现如下(instanceof
中的Java 17语法(:
class CustomZonedDateTimeConverter implements PropertyValueConverter {
private final DateTimeFormatter formatterWithZone = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSX");
private final DateTimeFormatter formatterWithoutZone = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSS");
@Override
public Object write(Object value) {
if (value instanceof ZonedDateTime zonedDateTime) {
return formatterWithZone.format(zonedDateTime);
} else {
return value;
}
}
@Override
public Object read(Object value) {
if (value instanceof String s) {
try {
return formatterWithZone.parse(s, ZonedDateTime::from);
} catch (Exception e) {
return formatterWithoutZone.parse(s, LocalDateTime::from).atZone(ZoneId.of("UTC"));
}
} else {
return value;
}
}
}
书写部分很简单,只需书写分区日期时间即可。在阅读时,我首先尝试解析一个分区日期时间。如果失败,我会尝试在没有任何区域的情况下进行解析。成功后,我将区域设置为UTC并返回值。
如果需要的话,你可以将其扩展到更多不同的格式