我有通过xml设置的spring数据mongo自定义转换器,如下所示
<mongo:mapping-converter id="mongoConverter" db-factory-ref="mongoDbFactory">
<mongo:custom-converters>
<mongo:converter ref="customWriteConverter" />
<mongo:converter ref="customReadConverter" />
</mongo:custom-converters>
</mongo:mapping-converter>
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg ref="mongoDbFactory"/>
<constructor-arg ref="mongoConverter"/>
</bean>
<bean id="customWriteConverter" class="package.WriteConverter" />
<bean id="customReadConverter" class="package.ReadConverter" />
在自定义读/写转换器中,我想重新使用spring-data-mongo的默认pojo转换器将某些属性保存为子文档。
考虑一个简化的例子 -
class A {
B b;
String var1;
int var2;
}
class B {
String var3;
String var4;
}
我想使用customWriteConverter
和customReadConverter
来处理A类的转换,但在我的自定义转换器中,我还想将B类的转换委托给spring-data-mongo的默认POJO转换器。 / p>
我该怎么做?我无法成功将MongoConverter或MongoTemplate自动装入自定义转换器,因为MongoConverter / MongoTemplate bean在尝试创建自定义转换器时正在进行中。是否可以访问默认转换器并在自定义转换器中使用它?
答案 0 :(得分:4)
此方法在MongoTemplate
类中用于获取默认转换器。
private static final MongoConverter getDefaultMongoConverter(MongoDbFactory factory) {
DbRefResolver dbRefResolver = new DefaultDbRefResolver(factory);
MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, new MongoMappingContext());
converter.afterPropertiesSet();
return converter;
}
MappingMongoConverter
不是最终版本,因此可以针对特定目的进行覆盖。正如我在上面的评论中所提到的,请查看this question以找出解决问题的方法。
答案 1 :(得分:3)
If you are converting TO mongo database and want to default some conversions, you could do something like this:
...
@Resource
private ObjectFactory<MappingMongoConverter>
mappingMongoConverterObjectFactory;
private MappingMongoConverter
mappingMongoConverter;
...
//Otherwise, use default MappingMongoConverter
//
if (result == null)
result =
getMappingMongoConverter()
.convertToMongoType(
value
);
...
MappingMongoConverter getMappingMongoConverter() {
if (mappingMongoConverter == null)
mappingMongoConverter =
mappingMongoConverterObjectFactory.getObject();
return
mappingMongoConverter;
}
The MappingMongoConverter cannot be directly @Resource (ed) in my case since it's in the process of being constructed when other converters are being built. So, Spring detects a circular reference. I am not sure if there is a better "lazy" method of doing this without all the run-around of ObjectFactory, getter method, and caching.
Now, if someone can figure out a method of defaulting to standard processing while going back (from DBObject to java Object) that would complete this circle.
答案 2 :(得分:0)
尝试在转换器中注入BeanFactory,并在转换方法中从BeanFactory中获取mongoTemplate ...
答案 3 :(得分:0)
这可能不是完全相同的用例,但是我不得不懒惰地修改现有的mongo文档(不使用$project等)。 基本上,我复制了Spring的getDefaultMongoConverter方法(此方法自此处早先的回答以来已更改,以后可能会再次更改),并添加了一个参数以传递自定义转换器。在创建自定义转换器本身(FooConverter)时,我为客户转换器传递了一个空列表(如果您有用于子文档的附加转换器,则可能会有所不同)。然后,在创建最终转换器时,我传入了FooConverter。
这是一些(未经测试的)示例代码。假设已启用自动配置,因此MongoDbFactory已经连接好了。否则,您将创建自己的MongoDbFactory bean,但其他所有操作都差不多。
@Bean
public MongoTemplate mongoTemplate(final MongoDbFactory mongoDbFactory) throws Exception {
FooReadConverter fooConverter = new FooReadConverter(mongoDbFactory);
MongoConverter converter = getMongoConverter(mongoDbFactory, List.of(fooConverter));
return new MongoTemplate(mongoDbFactory, converter);
}
/**
* Get a mongo converter
* @see org.springframework.data.mongodb.core.MongoTemplate#getDefaultMongoConverter
*/
static MongoConverter getMongoConverter(MongoDbFactory factory, List<?> customConverters) {
DbRefResolver dbRefResolver = new DefaultDbRefResolver(factory);
MongoCustomConversions conversions = new MongoCustomConversions(customConverters);
MongoMappingContext mappingContext = new MongoMappingContext();
mappingContext.setSimpleTypeHolder(conversions.getSimpleTypeHolder());
mappingContext.afterPropertiesSet();
MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, mappingContext);
converter.setCustomConversions(conversions);
converter.setCodecRegistryProvider(factory);
converter.afterPropertiesSet();
return converter;
}
@ReadingConverter
static class FooReadConverter implements Converter<Document, Foo> {
private final MongoConverter defaultConverter;
public FooReadConverter(MongoDbFactory dbFactory) {
this.defaultConverter = getMongoConverter(dbFactory, List.of());
}
@Override
public Foo convert(Document source) {
boolean isOldFoo = source.containsKey("someKeyOnlyInOldFoo");
Foo foo;
if (isOldFoo) {
OldFoo oldFoo = defaultConverter.read(OldFoo.class, source);
foo = oldFoo.toNewFoo();
} else {
foo = defaultConverter.read(Foo.class, source);
}
return foo;
}
}
答案 4 :(得分:0)
我知道已经很晚了,但是今天我刚刚遇到了这个问题,我认为在这里分享也许更好。
由于MongoTemplate(及其默认转换器)是在自定义转换器之后初始化的,因此无法将它们直接注入到我们的转换器中,但是我们可以通过在转换器中实现ApplicationContextAware
来访问它们。
访问mongoTemplate之后,我们可以通过分别调用mongoTemplate.getConverter().read
和mongoTemplate.getConverter().write
方法来委派读写转换。
我们来看一个例子。假设我们有两个POJO:
public class Outer {
public String var1;
public int var2;
public Inner inner;
}
public class Inner {
public String var3;
public String var4;
}
WriteConverter可能是这样的:
import org.bson.Document;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Component;
@Component
public class CustomWriteConverter implements Converter<Outer, Document>, ApplicationContextAware {
private ApplicationContext applicationContext;
private MongoTemplate mongoTemplate;
@Override
public Document convert(Outer source) {
// initialize the mongoTemplate
if (mongoTemplate == null) {
this. mongoTemplate = applicationContext.getBean(MongoTemplate.class);
}
// do some custom stuff
Document document = new Document();
document.put("var1", source.var1);
document.put("var2", source.var2);
// Using MongoTemplate's converters
Document inner = new Document();
mongoTemplate.getConverter().write(source.inner, inner);
document.put("inner", inner);
return document;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
和ReadConverter:
import org.bson.Document;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Component;
@Component
public class CustomReadConverter implements Converter<Document, Outer>, ApplicationContextAware {
private ApplicationContext applicationContext;
private MongoTemplate mongoTemplate;
@Override
public Outer convert(Document source) {
// initialize the mongoTemplate
if (mongoTemplate == null) {
this. mongoTemplate = applicationContext.getBean(MongoTemplate.class);
}
// do some custom stuff
Outer outer = new Outer();
outer.var1 = source.getString("var1");
outer.var2 = source.getInteger("var2");
// Using MongoTemplate's converters
Inner inner = mongoTemplate.getConverter().read(Inner.class, (Document) source.get("inner"));
outer.inner = inner;
return outer;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
mongoTemplate可以由多个线程初始化(因为它处于竞争状态),但是由于它的作用域为单例,所以没有问题。
现在唯一要做的就是注册我们的转换器。
答案 5 :(得分:0)
这里使用 spring-boot-starter-data-mongodb
版本 2.5.2
package com.example.mongo;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration;
import org.springframework.data.mongodb.core.convert.DbRefResolver;
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions.MongoConverterConfigurationAdapter;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
@Configuration
public class MongoConfig extends AbstractMongoClientConfiguration {
private @Value("${spring.data.mongodb.database}") String database;
private @Autowired MongoDatabaseFactory mongoDatabaseFactory;
@Override
protected String getDatabaseName() {
return database;
}
@Override
protected void configureConverters(MongoConverterConfigurationAdapter converterConfigurationAdapter) {
DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDatabaseFactory);
MongoConverter mongoConverter = new MappingMongoConverter(dbRefResolver, new MongoMappingContext());
converterConfigurationAdapter.registerConverters(customConverters(mongoConverter));
}
public List<Converter<?, ?>> customConverters(MongoConverter mongoConverter) {
MyCustomConverter custom = new MyCustomConverter(mongoConverter);
return List.of(custom);
}
}