5

I have a Java class having an Instant type of member variable:

public class SomeRecord {
    private String someId;

    private Instant someInstant;

    // getters and setters
}

I am using MongoTemplate to update the someInstant field in database:

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);
}

This works great if I am calling the method as:

updateSomeRecordBySomeId("SOME-ID", Instant.now()); persisting the field in DB as a Date type: "someInstant" : ISODate("2017-07-11T07:26:44.269Z")


Now the method may also be called as: updateSomeRecordBySomeId("SOME-ID", "2017-07-11T07:26:44.269Z");

In this case I get an exception as:

org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [java.time.Instant]

which makes complete sense. (It updates the field in the DB as String though. "someInstant" : "2017-07-11T07:26:44.269Z")


So I added a converter as follows:

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);
    }
}

After adding the above converter I am not getting ConverterNotFoundException any longer, but the field someInstant is being persisted as plain string: "someInstant" : "2017-07-11T07:26:44.269Z"

And that's what my question is. I know that the converter is being identified that is the reason I am not getting the exception anymore. But why the converter is not converting the String to Instant? Why the field is being persisted as plain String? Is the converter supplied incorrect? How to write converter for this case?

Note:

  • I have simplified the code to focus on the actual problem. In actual the method does not receive the someInstant field as parameter. So writing overloaded method is not going to be applicable here. Also any kind of instanceOf check inside the method won't work for the actual scenario. So the focus is on the question why the conversion not happening?

  • The actual data-store for us is DocumentDB, but we use DocumentDB with MongoDB API(as Spring Data does not support DocumentDB) for our database operations.

Vikas Prasad
  • 3,163
  • 5
  • 28
  • 39

1 Answers1

3

Your update logic is written in type agnostic way: you can pass any object type (Integer, Long, Boolean, String, Date, etc.) and it will be persisted in DB by overriding the existing value/type with new value and new type. Note: document oriented databased like MongoDB have no fixed schema, so stored data can arbitrary change data types.

The issue you had before you introduced converter with ConverterNotFoundException was not during update action, but during retrieval of updated object and setting it into your Java bean model: Java class defined someInstant property to be of an Instant / Date type, but database supplied a String value.

After you introduced a converter the issue of reading was solved, but only for String and Date types. If you update the someInstant property with some boolean value, you'll get back to the issue to read the object and map it to you Java bean.

Vladimir L.
  • 1,116
  • 14
  • 23
  • So you mean to say that while updating there is no conversion required at all, whatever type we supply will be updated because of the nature of No-SQL. The conversion is required to read back from DB into our Java POJO? – Vikas Prasad Jul 11 '17 at 23:51
  • This also makes sense why the field was getting updated in spite of the exception. Because there was no problem in the actual update. Problem was happening after the update operation. – Vikas Prasad Jul 11 '17 at 23:53
  • @VikasPrasad yes, naturally No-SQL (document oriented, schema free) databases allow that in general, but I was more referring to your function `updateSomeRecordBySomeId` The way it is written allows you to pass an object of any type, and since you do not use any ORM framework on top (like Morphia, or Spring data) and use pure MongoBD driver directly the code would allow to pass any type through. Something like this could help you to avoid initial mistake: `public SomeRecord updateSomeRecordBySomeId(final String someId, Instant someInstant)` – Vladimir L. Jul 12 '17 at 08:28
  • well, this code was just an example as I mentioned in the note. Anyway got your point!! – Vikas Prasad Jul 12 '17 at 09:20
  • @VikasPrasad does my explanation answers all your questions or do you have some questions left? – Vladimir L. Jul 12 '17 at 10:36
  • I am yet to try some things from my side. Post that will be accepting this as answer. – Vikas Prasad Jul 12 '17 at 12:51
  • @ Vladimir L. I asserted your answer by removing the converters, changing the return type of my update method to `void` and replacing the last line with `operations.updateFirst(query, update, SomeRecord.class);` By doing this I am just asking for update and no retrieval of the updated document. And as you mentioned, now that we are not doing the read operation anymore, I did not get the exception. The document updates successfully. So my understanding of converters being used at the time of persist was wrong. Converters are being used while reading from DB. Thanks – Vikas Prasad Jul 13 '17 at 16:35