Java 强制Spring数据MongoDB将所有日期读取为Joda DateTime类型
我正在尝试保留以下文档:Java 强制Spring数据MongoDB将所有日期读取为Joda DateTime类型,java,spring,mongodb,datetime,Java,Spring,Mongodb,Datetime,我正在尝试保留以下文档: class Document { ... private Map data; ... } 要存储日期,我使用Joda的DateTime对象: data.put("date", DateTime.now()); 我正在使用MongoRepository来保存数据。写作非常有效,我可以看到mongoDB有一个日期类型 但是,当我读取数据时,我的地图不包含DateTime,而是一个Date对象 我明白了,没有什么可以告诉Spring将要读取的对象的
class Document {
...
private Map data;
...
}
要存储日期,我使用Joda的DateTime对象:
data.put("date", DateTime.now());
我正在使用MongoRepository来保存数据。写作非常有效,我可以看到mongoDB有一个日期类型
但是,当我读取数据时,我的地图不包含DateTime,而是一个Date对象
我明白了,没有什么可以告诉Spring将要读取的对象的类型从Date(默认)更改为DateTime,因为我没有读取具体的类型
有没有办法让MongoTemplate/MongoRepository在读取时始终将java.util.Date转换为Joda DateTime
这可能与转换器有关,但我还没有找到一个如何实现这一点的示例
谢谢。
-AP您必须实现自己的转换器,然后在启用MongoDB存储库的类中重写
customConversions()
(@EnableMongoRepositories
)
它还取决于您使用的Spring数据版本;到目前为止,我还记得还有一个DateTimeConverters
这就像是,但是实现Spring的
转换器
接口。因此,最后我不得不做出一些妥协。由于我使用映射来保存任意数据,因此无法调用转换器,因为任何东西都可以设置为对象,因此Spring数据不需要执行从日期到日期时间的任何转换
我没有使用泛型映射,而是创建了自己的类型,它只是从映射扩展而来:
public class EventData extends HashMap<String, Object> {
...
}
现在,有了EventData,我可以创建一个自定义映射器:
@ReadingConverter
public class ConsumerHandlerEventDataReadConverter implements Converter<DBObject, EventData> {
@Resource
private MappingMongoConverter
mappingMongoConverter;
@Override
public EventData convert(final DBObject source) {
//First, use the general mapping mongo converter to read the source as it would normally be read
//
final EventData
eventData =
mappingMongoConverter
.read(
EventData.class,
source
);
//Now replaces all occurances of Date in EventData with DateTime
//
for (final Map.Entry<String, Object> entry : eventData.entrySet()) {
//Get the value of this entry
//
final Object
entryValue =
entry
.getValue();
//If it's a date, replace with Datetime
//
if (entryValue instanceof Date) {
entry
.setValue(
new DateTime(
entryValue
)
);
}
}
//Return result
//
return
eventData;
}
}
理想情况下,我希望以某种方式创建一个映射器,MappingMongoConverter在处理日期类型时可以始终参考该映射器,而不是专门在EventData的上下文中,但是,查看Spring数据中的readMap(…)代码,我发现这不会发生:
private Object getPotentiallyConvertedSimpleRead(Object value, Class<?> target) {
if (value == null || target == null || target.isAssignableFrom(value.getClass())) {
return value;
}
if (conversions.hasCustomReadTarget(value.getClass(), target)) {
return conversionService.convert(value, target);
}
if (Enum.class.isAssignableFrom(target)) {
return Enum.valueOf((Class<Enum>) target, value.toString());
}
return conversionService.convert(value, target);
}
私有对象getPotentiallyConvertedSimpleRead(对象值,类目标){
if(value==null | | target==null | | target.isAssignableFrom(value.getClass())){
返回值;
}
if(conversions.hasCustomReadTarget(value.getClass(),target)){
返回conversionService.convert(值,目标);
}
if(Enum.class.isAssignableFrom(目标)){
返回Enum.valueOf((类)target,value.toString());
}
返回conversionService.convert(值,目标);
}
如果target.isAssignableFrom(value.getClass()),上面的代码将不会调用任何映射程序,并且由于所有内容都可分配给对象,因此根本无法注册自定义映射程序。代码中的下一条语句确实会检查conversions.hasCustomReadTarget是否正确,但我们始终没有找到它
无论如何,为下一次记录这一点,也为任何试图用转换器做类似事情的人
-AP为什么要使用映射而不是特定的嵌入式类?我通过Jackson读取数据,虽然JSON对象的某些部分定义良好(时间戳、id、类型),但其他部分完全灵活。我需要能够在MongoDB中读取和存储此对象。最后,我想实现一个“混合”对象,它有一些已定义的字段和一些任意字段,但还没有实现到这一步。到目前为止,我所拥有的只是一张包含所有字段的地图。这是可行的,但并不完全可行。如果EventData包含一个对象,而该对象又包含另一个映射,则该映射中的日期不会转换。此外,由于我在转换器中使用不同的转换器(未配置自定义转换),因此不会应用嵌套转换。如果我使用相同的映射器(已注册转换器),我将进入以堆栈溢出结束的无限循环。所以,我真正想做的只是,不加区别地,将Date的任何实例更改为DateTime。有没有一种方法可以用SpringData实现这一点,而我却找不到?
@Configuration
public class ConsumerHandlerMongoDBConfiguration {
...
@Bean
public CustomConversions customConversions() {
return
new CustomConversions(
Arrays.asList(
consumerHandlerEventDataReadConverter()
)
);
}
@Bean
@Description("Mapping mongo converter for the event")
public MappingMongoConverter consumerProcessHandlerMappingMongoConverter() {
final MappingMongoConverter
converter =
new MappingMongoConverter(
mongoDBConfiguration.dbRefResolver(),
mongoDBConfiguration.mongoMappingContext()
);
converter
.setTypeMapper(
mongoDBConfiguration.mongoTypeMapper()
);
converter
.setCustomConversions(
customConversions()
);
return
converter;
}
...
}
private Object getPotentiallyConvertedSimpleRead(Object value, Class<?> target) {
if (value == null || target == null || target.isAssignableFrom(value.getClass())) {
return value;
}
if (conversions.hasCustomReadTarget(value.getClass(), target)) {
return conversionService.convert(value, target);
}
if (Enum.class.isAssignableFrom(target)) {
return Enum.valueOf((Class<Enum>) target, value.toString());
}
return conversionService.convert(value, target);
}