在Spring Boot和ElasticSearch中使用Instant,LocalDateTime和ZonedDateTime

时间:2019-05-06 14:35:24

标签: java spring-boot elasticsearch jackson spring-data

我正在将Spring Boot 2.1.4和Spring Data Jest与ElasticSearch结合使用。最初,我使用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数据格式?

我尝试了以下方法:

  1. 自定义对象映射器,如下所示:

    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);
        }
    }
    
  2. 向对象映射器添加以下内容:

    objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
    

任何帮助或我出错的指针将不胜感激。

3 个答案:

答案 0 :(得分: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中,然后又注入到框架使用的JestElasticSearchTemplate中。

CustomEntityMapper中,您可以自动连线默认的ObjectMapper(它将自动添加JavaTimeModule),也可以自行配置一个。

答案 1 :(得分:1)

设法使其与Spring Boot 2.1.4和Spring Data Jest一起使用。这是我所做的:

  1. 示例域对象:

    @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
    }
    
  2. 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);
            }
    
        }
    }
    
  3. ElasticSearch中的结果:

    Screenshot of results in ElasticSearch

希望这会有所帮助。

答案 2 :(得分:-1)

根据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进行附加