How to store Java 8 Instant to MongoDB as date type using Spring MongoTemplate?
I have a Java class that has a member variable Instant
:
public class SomeRecord {
private String someId;
private Instant someInstant;
// getters and setters
}
I am using MongoTemplate to update a field someInstant
in a 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 call the method like:
updateSomeRecordBySomeId("SOME-ID", Instant.now());
storing the field in the DB as a type Date
:
"someInstant" : ISODate("2017-07-11T07:26:44.269Z")
Now this method can also be called as follows:
updateSomeRecordBySomeId("SOME-ID", "2017-07-11T07:26:44.269Z");
In this case, I am getting an exception like:
org.springframework.core.convert.ConverterNotFoundException: There is no converter that can be converted from type [java.lang.String] to type [java.time.Instant]
which makes sense. (It updates the field in the DB like String
though. "someInstant" : "2017-07-11T07:26:44.269Z"
)
So I added a converter like this:
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 don't get any more ConverterNotFoundException
, but the field is someInstant
saved as a simple string:"someInstant" : "2017-07-11T07:26:44.269Z"
And that's my question. I know the converter is identified, so I no longer get the exception. But why doesn't the converter convert String
to Instant
? Why is the field saved as normal String
? Is the inverter installed correctly? How do I write a converter for this case?
Note:
-
I've simplified the code to focus on the real problem. The method does not actually receive a field
someInstant
as a parameter. Therefore, you won't write an overloaded method here. Also any type of validationinstanceOf
within a method will not work for a real scenario. So the focus is on why the conversion isn't happening? -
The actual data store for us is DocumentDB, but we are using DocumentDB with the MongoDB API (as Spring Data does not support DocumentDB) for our database operations.
source to share
Your update logic is written as type agnostic: you can pass any type of object (Integer, Long, Boolean, String, Date, etc.) and it will persist in the DB, overriding the existing value / type with the new value and a new type . Note. Documented databases like MongoDB do not have a fixed schema, so the stored data can change data types arbitrarily.
The problem before entering the converter with ConverterNotFoundException
was not during the update action, but during retrieving the updated object and installing it in your Java bean model: Java class property someInstant
t22> / Date
, but the database has a value String
.
After entering the converter, the reading problem was resolved, but only for types String
and Date
. If you update the property someInstant
with some value boolean
, you are back to the problem to read the object and map it to you Java bean.
source to share