Как автоматически подключить mongoTemplate к конвертеру пользовательского типа?

Я пытаюсь создать конвертер, который будет извлекать объект из БД по его ObjectId. Но mongoTemplate всегда пуст в конвертере:

org.springframework.core.convert.ConversionFailedException:

Не удалось преобразовать тип org.bson.types.ObjectId в тип com.atlas.mymodule.datadomain.MyObject для значения «1300000000000000000000013»;

вложенным исключением является 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 (добавлено определение bean-компонента 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?

Спасибо.


person S2201    schedule 21.12.2015    source источник
comment
где вы определяете bean-компонент mongoTemplate?   -  person Will    schedule 21.12.2015
comment
Он определен в AbstractMongoConfiguration: @Bean public MongoTemplate mongoTemplate() throws Exception { return new MongoTemplate(mongoDbFactory(), mappingMongoConverter()); }   -  person S2201    schedule 21.12.2015
comment
не могли бы вы решить проблему с циклической ссылкой? Столкнулся с такой же проблемой сейчас   -  person Manuel Waltschek    schedule 06.11.2020


Ответы (1)


ObjectIdToMyObjectConverter не является компонентом Spring. Если вы хотите, чтобы @Autowired работал, создайте ObjectIdToMyObjectConverter как компонент Spring, например:

@Bean
public ObjectIdToMyObjectConverter objectIdToMyObjectConverter() {
    return new ObjectIdToMyObjectConverter());
}

и @Autowire его в вашей конфигурации.

После обновления @Savash

Я не уделил должного внимания вашим настройкам.

То, что вы видите, происходит потому, что вы пытаетесь создать MongoTemplate, который зависит от CustomConversions, и в то же время CustomConversions зависят от MongoTemplate, spring не может и не должен этого делать.

Как решение:

  1. Вы можете создавать свои CustomConversions с помощью ApplicationContextAware и лениво извлекать ссылку MongoTemplate при первом вызове.
  2. Я думал, вы используете CustomConversions как часть spring-integration или что-то в этом роде. Если это так, он не должен быть частью конвертеров для Mongo. Если вам это нужно как MongoConverters, вы делаете что-то действительно странное.

Для какого именно варианта использования вам это нужно?

Следующие комментарии:

Правильно ли я понимаю, что вы хотите, чтобы MongoTemplate читал объект со ссылкой на пользователя как объект пользователя и записывал объект со значением пользователя в качестве ссылки на пользователя?

Я думаю.

  1. У вас плохая модель данных (вы пытаетесь эмулировать операцию JOIN в своем MongoTemplate, что означает, что вам что-то не хватает в вашей модели данных, и вы не должны так работать с монго).
  2. Просто вызовите пользователя явно, когда вам это нужно, не перегружайте свою БД дополнительной работой, у вас будут проблемы с производительностью.
  3. Вы можете использовать другой объект, который вы дополните текущим пользователем по мере необходимости.
  4. Может быть, SQL и ORM, такие как Hibernate, являются для вас лучшим подходом?
  5. Попробуйте Hibernate OGM для ваших целей, он может обеспечить функциональность, которая вам нужна (хотя не уверен, не работал с ним)
person mavarazy    schedule 21.12.2015
comment
Я пытаюсь получить объект из базы данных по его ObjectId во время сопоставления. У меня есть поле типа {user: ObjectId(some_mongo_id),...}, и я хочу иметь сам объект вместо ObjectId при гидратации базового объекта. - person S2201; 21.12.2015
comment
Правильно ли я понимаю, что вы хотите, чтобы MongoTemplate читал объект со ссылкой на пользователя как объект пользователя и записывал объект со значением пользователя в качестве ссылки на пользователя? - person mavarazy; 21.12.2015
comment
Спасибо за повтор! 1,4 - вы правы, но я работаю над существующей базой данных, которая обслуживает другие приложения, поэтому изменения невозможны. 5 - Hibernate OGM пропускает другие вещи, которые нам нужны... 2 - поле пользователя содержит только идентификатор, и я не могу полагаться на имя поля, чтобы угадать, к какой коллекции оно принадлежит (в данной БД нет никакого соглашения об именах)... Я вижу преобразователи более подходящими. 3 - Можете ли вы объяснить, что вы имеете в виду здесь? - person S2201; 22.12.2015