Как автоматически подключить mongoTemplate в конвертер пользовательских типов?
Я пытаюсь создать конвертер, который будет извлекать объект из БД по его ObjectId. Но mongoTemplate всегда пуст в конвертере:
org.springframework.core.convert.ConversionFailedException:
Не удалось преобразовать тип org.bson.types.ObjectId в тип com.atlas.mymodule.datadomain.MyObject для значения '130000000000000000000013';
Вложенным исключением является java.lang.NullPointerException
Код:
@Component
public class ObjectIdToMyObjectConverter implements Converter<ObjectId, MyObject> {
@Autowired
private MongoTemplate mongoTemplate; // null ???
public MyObject convert(ObjectId objectId) {
return mongoTemplate.findById(objectId, MyObject.class); // <- NullPointerException
}
}
Конфигурация:
@Configuration
@ComponentScan
@EnableMongoRepositories
public abstract class MyModuleConfiguration extends AbstractMongoConfiguration {
@Override
public MongoClient mongo() throws Exception {
List<MongoCredential> mongoCredential = getMongoCredentials();
return mongoCredential == null ?
new MongoClient(getMongoServerAddresses()) :
new MongoClient(getMongoServerAddresses(), mongoCredential, getMongoClientOptions());
}
protected abstract List<MongoCredential> getMongoCredentials();
protected abstract MongoClientOptions getMongoClientOptions();
protected abstract List<ServerAddress> getMongoServerAddresses() throws UnknownHostException;
@Bean
public ObjectIdToMyObjectConverter objectIdToMyObjectConverter() {
return new ObjectIdToMyObjectConverter());
}
@Override
public CustomConversions customConversions() {
List<Converter<?, ?>> converters = new ArrayList<Converter<?, ?>>();
converters.add(objectIdToMyObjectConverter());
return new CustomConversions(converters);
}
}
Тестовая конфигурация:
public class MyModuleTestConfiguration extends MyModuleConfiguration {
// overrides abstract methods, defines connection details...
}
Обновить:
Я обновил код в соответствии с предложением @mavarazy (добавлено определение компонента ObjectIdToMyObjectConverter), но получил исключение:
Ошибка создания bean-компонента с именем mongoTemplate: запрашиваемый bean-компонент находится в процессе создания: существует неразрешимая циклическая ссылка?
Полное исключение:
Error creating bean with name 'mongoTemplate' defined in com.atlas.MyModule.MyModuleTestConfiguration:
Bean instantiation via factory method failed;
nested exception is org.springframework.beans.BeanInstantiationException:
Failed to instantiate [org.springframework.data.mongodb.core.MongoTemplate]: Factory method 'mongoTemplate' threw exception;
nested exception is org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'mappingMongoConverter' defined in com.atlas.MyModule.MyModuleTestConfiguration: Bean instantiation via factory method failed;
nested exception is org.springframework.beans.BeanInstantiationException:
Failed to instantiate [org.springframework.data.mongodb.core.convert.MappingMongoConverter]: Factory method 'mappingMongoConverter' threw exception;
nested exception is org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'mongoMappingContext' defined in com.atlas.MyModule.MyModuleTestConfiguration: Bean instantiation via factory method failed;
nested exception is org.springframework.beans.BeanInstantiationException:
Failed to instantiate [org.springframework.data.mongodb.core.mapping.MongoMappingContext]: Factory method 'mongoMappingContext' threw exception;
nested exception is org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'customConversions' defined in com.atlas.MyModule.MyModuleTestConfiguration: Bean instantiation via factory method failed;
nested exception is org.springframework.beans.BeanInstantiationException:
Failed to instantiate [org.springframework.data.mongodb.core.convert.CustomConversions]: Factory method 'customConversions' threw exception;
nested exception is org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'objectIdToMyObjectConverter': Injection of autowired dependencies failed;
nested exception is org.springframework.beans.factory.BeanCreationException:
Could not autowire field: private org.springframework.data.mongodb.core.MongoTemplate com.atlas.MyModule.ObjectIdToMyObjectConverter.mongoTemplate;
nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'mongoTemplate': Requested bean is currently in creation: Is there an unresolvable circular reference?
Благодарю.
1 ответ
ObjectIdToMyObjectConverter не является пружинным компонентом. Если вы хотите, чтобы @Autowired работал, создайте ObjectIdToMyObjectConverter как бин Spring, например:
@Bean
public ObjectIdToMyObjectConverter objectIdToMyObjectConverter() {
return new ObjectIdToMyObjectConverter());
}
и @Autowire это в вашей конфигурации.
После обновления @Savash
Я не уделил достаточного внимания вашим настройкам.
То, что вы видите, происходит потому, что вы пытаетесь создать MongoTemplate, который зависит от CustomConversions, и в то же время CustomConversions зависят от MongoTemplate, Spring не может и не должна этого делать.
Как решение:
- Вы можете создавать свои CustomConversions с ApplicationContextAware и извлекать ссылку MongoTemplate лениво при первом вызове.
- Я думал, что вы используете CustomConversions как часть Spring-интеграции или что-то. Если это так, он не должен быть частью конвертеров для Монго. Если вам это нужно как MongoConverters, вы делаете что-то действительно странное.
Для чего нужен конкретный вариант использования?
Следующие комментарии:
Правильно ли я понимаю, что вы хотите, чтобы MongoTemplate считывал объект со ссылкой на пользователя в качестве объекта пользователя и записывал объект со значением пользователя в качестве ссылки на пользователя?
Я думаю.
- У вас плохая модель данных (вы пытаетесь эмулировать операцию JOIN в вашем MongoTemplate, что означает, что вы что-то упустили в своей модели данных, и это не то, как вы должны работать с mongo).
- Просто вызовите пользователя явно, когда вам это нужно, не перегружайте свою БД дополнительной работой, у вас будут проблемы с производительностью
- При необходимости вы можете использовать другой объект, который вы обогатите текущим пользователем.
- Может быть, SQL & ORM, такой как Hibernate, лучше для вас?
- Попробуйте Hibernate OGM для своих целей, он может обеспечить функциональность, которая вам нужна (но не уверен, что не работал с ней)