如何使用 Spring MongoTemplate 将 Java 8 Instant 保存为 MongoDB 作为日期类型?
How to save Java 8 Instant to MongoDB as Date type using Spring MongoTemplate?
我有一个 Java class 具有 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: No
converter found capable of converting from type [java.lang.String] to
type [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 with MongoDB API(因为 Spring 数据不支持 DocumentDB)用于我们的数据库操作。
您的更新逻辑是以类型不可知的方式编写的:您可以传递任何对象类型(Integer、Long、Boolean、String、Date 等),它将通过覆盖现有的 value/type 新值和新类型。注意:像MongoDB这样的面向文档的数据库没有固定的模式,所以存储的数据可以任意改变数据类型。
在使用 ConverterNotFoundException
引入转换器之前,您遇到的问题不是在更新操作期间,而是在检索更新对象并将其设置到 Java bean 模型期间:Java class 将 someInstant
属性 定义为 Instant
/ Date
类型,但数据库提供了 String
值。
引入转换器后,读取问题已解决,但仅限于 String
和 Date
类型。如果您使用某些 boolean
值更新 someInstant
属性,您将回到问题以读取对象并将其映射到您的 Java bean。
我有一个 Java class 具有 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: No converter found capable of converting from type [java.lang.String] to type [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 with MongoDB API(因为 Spring 数据不支持 DocumentDB)用于我们的数据库操作。
您的更新逻辑是以类型不可知的方式编写的:您可以传递任何对象类型(Integer、Long、Boolean、String、Date 等),它将通过覆盖现有的 value/type 新值和新类型。注意:像MongoDB这样的面向文档的数据库没有固定的模式,所以存储的数据可以任意改变数据类型。
在使用 ConverterNotFoundException
引入转换器之前,您遇到的问题不是在更新操作期间,而是在检索更新对象并将其设置到 Java bean 模型期间:Java class 将 someInstant
属性 定义为 Instant
/ Date
类型,但数据库提供了 String
值。
引入转换器后,读取问题已解决,但仅限于 String
和 Date
类型。如果您使用某些 boolean
值更新 someInstant
属性,您将回到问题以读取对象并将其映射到您的 Java bean。