在 Spring Boot 和 ElasticSearch 中使用 Instant、LocalDateTime 和 ZonedDateTime
Posted
技术标签:
【中文标题】在 Spring Boot 和 ElasticSearch 中使用 Instant、LocalDateTime 和 ZonedDateTime【英文标题】:Using Instant, LocalDateTime and ZonedDateTime with Spring Boot and ElasticSearch 【发布时间】:2019-09-24 05:10:42 【问题描述】:我正在使用带有 ElasticSearch 的 Spring Boot 2.1.4 和 Spring Data Jest。我最初使用 Java Date 来处理一些带有以下注释的属性:
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern ="yyyy-MM-dd'T'HH:mm:ss.SSSZZ")
这样保存到 ElasticSearch 中:
"creationDate": "2019-04-10T14:49:05.672+0000"
现在,我正在从 Date 迁移到 LocalDateTime 和 ZonedDateTime。现在将数据保存到 ElasticSearch 时,我保存了以下属性:
"creationDate":
"dayOfYear": 123,
"dayOfWeek": "FRIDAY",
"month": "MAY",
"dayOfMonth": 3,
"year": 2019,
"monthValue": 5,
"hour": 11,
"minute": 54,
"second": 12,
"nano": 238000000,
"chronology":
"id": "ISO",
"calendarType": "iso8601"
,
我需要做些什么来更改它,以便获得与以前的 LocalDateTime 和 ZonedDateTime 相同的 ElasticSearch 数据格式?
我尝试了以下方法:
自定义对象映射器如下:
public class CustomEntityMapper implements EntityMapper
private final ObjectMapper objectMapper;
public CustomEntityMapper(ObjectMapper objectMapper)
this.objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
objectMapper.registerModule(new CustomGeoModule());
objectMapper.registerModule(new JavaTimeModule());
@Override
public String mapToString(Object object) throws IOException
return objectMapper.writeValueAsString(object);
@Override
public <T> T mapToObject(String source, Class<T> clazz) throws IOException
return objectMapper.readValue(source, clazz);
在对象映射器中添加以下内容:
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
任何我出错的帮助或指示将不胜感激。
【问题讨论】:
永远不要使用LocalDateTime
来表示片刻,因为该类故意缺少任何时区或UTC偏移量的癌症。使用Instant
、OffsetDateTime
或ZonedDateTime
表示时间轴上的特定点。
感谢您的评论,为什么不应该使用 LocalDateTime?我的示例是创建日期的字段。
您的 JSON 输入代表一个时刻,即时间线上的一个特定点。 LocalDateTime
不能代表片刻。阅读课堂文档。在 Stack Overflow 上阅读有关该主题的许多其他帖子。
仅供参考,您所说的“ElasticSearch 数据格式”实际上是标准的ISO 8601 格式,用于以文本方式表示日期时间值。在解析/生成字符串时,这些格式默认在 java.time 类中使用。
您的示例 ISO 8601 字符串与您的示例 JSON 不匹配。提供示例数据时,请务必准确。
【参考方案1】:
设法让它与 Spring Boot 2.1.4 和 Spring Data Jest 一起工作。这是我所做的:
域对象示例:
@Document(indexName = "datetest")
public class DateTest
@Id
private String id;
@Field(type = FieldType.Date, format = DateFormat.custom, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZZ")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern ="yyyy-MM-dd'T'HH:mm:ss.SSSZZ", timezone = "UTC")
private Instant instant = Instant.now();
@Field(type = FieldType.Date, format = DateFormat.custom, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZZ")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern ="yyyy-MM-dd'T'HH:mm:ss.SSSZZ")
private ZonedDateTime zonedDateTime = ZonedDateTime.now();
@Field(type = FieldType.Date, format = DateFormat.custom, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern ="yyyy-MM-dd'T'HH:mm:ss.SSS")
private LocalDateTime localDateTime = LocalDateTime.now();
// getters/setters
ElasticSearch/JEST 配置:
@Configuration
public class ESConfig
@Bean
public EntityMapper getEntityMapper()
return new CustomEntityMapper();
@Bean
@Primary
public ElasticsearchOperations elasticsearchTemplate(final JestClient jestClient,
final ElasticsearchConverter elasticsearchConverter,
final SimpleElasticsearchMappingContext simpleElasticsearchMappingContext, EntityMapper mapper)
return new JestElasticsearchTemplate(jestClient, elasticsearchConverter,
new DefaultJestResultsMapper(simpleElasticsearchMappingContext, mapper));
public class CustomEntityMapper implements EntityMapper
private final ObjectMapper objectMapper;
public CustomEntityMapper()
objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
objectMapper.registerModule(new CustomGeoModule());
objectMapper.registerModule(new JavaTimeModule());
@Override
public String mapToString(Object object) throws IOException
return objectMapper.writeValueAsString(object);
@Override
public <T> T mapToObject(String source, Class<T> clazz) throws IOException
return objectMapper.readValue(source, clazz);
ElasticSearch 中的结果:
希望这会有所帮助。
【讨论】:
【参考方案2】:那是因为spring-data-jest
使用了DefaultEntityMapper
(Spring Data 的一部分),它创建了自己的ObjectMapper
,而不使用 Spring boot 提供的那个。这可以在this related question 中看到。
通过定义自己的EntityMapper
,例如CustomEntityMapper
,您的解决方案就走在了正确的轨道上。但是,spring-data-jest
将此映射器包装到名为 DefaultJestResultsMapper
的类中,然后由名为 JestElasticsearchTemplate
的 bean 使用。
所以,也许你应该这样做:
@Bean
public JestResultsMapper resultMapper(CustomEntityMapper entityMapper)
return new DefaultJestResultsMapper(entityMapper);
@Bean
public JestElasticSearchTemplate template(JestClient client, JestResultsMapper resultsMapper)
return new JestElasticSearchTemplate(client, resultsMapper);
这应该将您的CustomEntityMapper
注入到JestResultsMapper
中,而JestResultsMapper
又注入到框架使用的JestElasticSearchTemplate
中。
在CustomEntityMapper
中,您可以自动连接默认的ObjectMapper
(将自动添加JavaTimeModule
),也可以自行配置。
【讨论】:
万岁!上面的部分是我所缺少的。有很多答案和不同的配置,所以我将它剥离回一个基本的 Spring Boot 2 测试应用程序并让它工作。我将在上面添加我的示例类和配置。希望对其他人有所帮助,感谢您的支持!【参考方案3】:根据 Spring Boot 版本 2 中的this answer,它应该可以根据您的需要开箱即用,以便从 java.time 对象生成字符串
如果你有
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.4.0
在application.properties中作为依赖和下面的行
spring.jackson.serialization.write_dates_as_timestamps=false
所以唯一剩下的就是将时区表示法添加到默认字符串中,而默认字符串不会有它。
如果标准格式化程序不适合您,可以随时编写自己的序列化器/反序列化器并按照here 解释的方式附加它
【讨论】:
这可以配置默认的ObjectMapper
,但是spring-data-jest框架不使用这个映射器,所以这个解决方案不起作用。
@g00glen00b 感谢您的澄清,我不知道这一点。我试图帮助从其他线程收集信息。以上是关于在 Spring Boot 和 ElasticSearch 中使用 Instant、LocalDateTime 和 ZonedDateTime的主要内容,如果未能解决你的问题,请参考以下文章
Spring Boot:Spring Boot整合Logback和PageHelper
spring-boot-starter-jta-atomikos 和 spring-boot-starter-batch
spring boot 系列之六:@Conditional和spring boot的自动配置
Spring Boot系列 Spring Boot介绍和基础POM文件