Как сохранить Java 8 Instant в MongoDB как тип Date, используя Spring MongoTemplate?

У меня есть класс Java, имеющий Instant тип переменной-члена:

public class SomeRecord {
    private String someId;

    private Instant someInstant;

    // getters and setters
}

Я использую MongoTemplate для обновления someInstant поле в базе данных:

public SomeRecord updateSomeRecordBySomeId(final String someId, Object someInstant) {
        Query query = new Query();
        query.addCriteria(Criteria.where("someId").is(someId));

        Update update = new Update();
        update.set("someInstant", someInstant);

        return operations.findAndModify(query, update, new FindAndModifyOptions().returnNew(true), SomeRecord.class);
}

Это прекрасно работает, если я вызываю метод как:

updateSomeRecordBySomeId("SOME-ID", Instant.now()); сохраняя поле в БД как Date тип: "someInstant" : ISODate("2017-07-11T07:26:44.269Z")


Теперь метод также может быть вызван как: updateSomeRecordBySomeId("SOME-ID", "2017-07-11T07:26:44.269Z");

В этом случае я получаю исключение как:

org.springframework.core.convert.ConverterNotFoundException: не найден конвертер, способный преобразовать тип [java.lang.String] в тип [java.time.Instant]

что имеет полный смысл. (Обновляет поле в БД как String хоть. "someInstant" : "2017-07-11T07:26:44.269Z")


Поэтому я добавил конвертер следующим образом:

MongoConfig.java:

@Configuration
@ComponentScan(basePackages = {"dao package path here"})
public class MongoConfig {
    @Autowired
    private MongoDbFactory mongoDbFactory;

    @Bean
    public MongoTemplate mongoTemplate() {
        MappingMongoConverter converter = new MappingMongoConverter(new DefaultDbRefResolver(mongoDbFactory),
                new MongoMappingContext());

        converter.setCustomConversions(new CustomConversions(Collections.singletonList(new StringToInstantConverter())));

        return new MongoTemplate(mongoDbFactory, converter);
    }
}

StringToInstantConverter.java:

public class StringToInstantConverter implements Converter<String, Instant> {
    @Override
    public Instant convert(String utcString) {
        // TODO: Make it generic for any time-zone
        return Instant.parse(utcString);
    }
}

После добавления вышеуказанного конвертера я не получаю ConverterNotFoundException больше, но поле someInstant сохраняется как простая строка: "someInstant" : "2017-07-11T07:26:44.269Z"

И это то, что мой вопрос. Я знаю, что конвертер идентифицируется, поэтому я больше не получаю исключение. Но почему конвертер не конвертирует String в Instant? Почему поле сохраняется как простое String? Неверно ли установлен преобразователь? Как написать конвертер для этого случая?

Замечания:

  • Я упростил код, чтобы сосредоточиться на актуальной проблеме. На самом деле метод не получает someInstant поле в качестве параметра. Поэтому написание перегруженного метода здесь неприменимо. Также любой вид instanceOf проверка внутри метода не будет работать для реального сценария. Таким образом, акцент делается на вопрос, почему конверсия не происходит?

  • Фактическим хранилищем данных для нас является DocumentDB, но мы используем DocumentDB с API MongoDB (поскольку Spring Data не поддерживает DocumentDB) для операций с нашей базой данных.

1 ответ

Решение

Ваша логика обновления написана независимым от типа способом: вы можете передать любой тип объекта (Integer, Long, Boolean, String, Date и т. Д.), И он будет сохранен в БД путем переопределения существующего значения / типа новым значением и новым типом, Примечание: ориентированные на документы базы данных, такие как MongoDB, не имеют фиксированной схемы, поэтому хранимые данные могут произвольно изменять типы данных.

Проблема, с которой вы столкнулись до того, как представили конвертер с ConverterNotFoundException не во время действия обновления, а во время извлечения обновленного объекта и установки его в вашу модель Java-компонента: определен класс Java someInstant свойство быть Instant / Date тип, но база данных предоставлена String значение.

После того, как вы представили конвертер, проблема чтения была решена, но только для String а также Date типы. Если вы обновите someInstant собственность с некоторыми boolean значение, вы вернетесь к проблеме, чтобы прочитать объект и сопоставить его с Java-бином.

Другие вопросы по тегам