Использование Instant, LocalDateTime и ZonedDateTime с Spring Boot и ElasticSearch - PullRequest
1 голос
/ 06 мая 2019

Я использую 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"

Теперь я в процессе миграции сДата в 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"
    }
},

Что мне нужно сделать, чтобы изменить его, чтобы я получил тот же формат данных ElasticSearch, что и раньше для LocalDateTime и ZonedDateTime?

Я пробовал следующее:

  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. Добавление следующего к объектуmapper:

    objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
    

Любая помощь или указатели, где я иду не так, будут оценены.

Ответы [ 3 ]

2 голосов
/ 07 мая 2019

Это потому, что spring-data-jest использует DefaultEntityMapper (часть Spring Data), который создает свой собственный ObjectMapper и не использует тот, который предоставляется Spring boot. Это можно увидеть в этом связанном вопросе .

Вы на правильном пути со своим решением, определив свой собственный EntityMapper, например CustomEntityMapper. Однако spring-data-jest оборачивает этот преобразователь в класс с именем DefaultJestResultsMapper, который затем используется компонентом с именем JestElasticsearchTemplate.

Итак, вероятно, вы должны сделать что-то вроде этого:

@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 голос
/ 08 мая 2019

удалось заставить его работать с 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

Надеюсь, это поможет.

0 голосов
/ 06 мая 2019

Согласно этому ответу из версии 2 Spring Boot, он должен работать "из коробки", как вы хотите, с точки зрения создания строки из объектов java.time

Если у вас есть

com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.4.0

как зависимость и нижняя строка в application.properties

spring.jackson.serialization.write_dates_as_timestamps=false

Таким образом, остается только добавить обозначение часового пояса в строку по умолчанию, в которой его нет.

Если стандартные форматеры не будут работать для вас, вы всегда можете написать свой собственный сериализатор / десериализатор и прикрепить его, как описано здесь

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...