Description
Jean-Pierre Bergamin opened DATAMONGO-1924 and commented
We're trying to use multiple MongoDB databases in our spring boot project. To do that we create two MongoTemplate
instances and use the @EnableMongoRepositories
annotation to have different repositores in different packages use one of the two MongoTemplate
s:
@Configuration
public class MongoConfig {
public static final String A_MONGO_TEMPLATE = "aMongoTemplate";
public static final String B_MONGO_TEMPLATE = "bMongoTemplate";
@Configuration
@EnableMongoRepositories(basePackageClasses = RepoInDbA.class, mongoTemplateRef = A_MONGO_TEMPLATE)
public static class AMongoConfig {
}
@Configuration
@EnableMongoRepositories(basePackageClasses = RepoInDbB.class, mongoTemplateRef = B_MONGO_TEMPLATE)
public static class BMongoConfig {
}
@Bean(name = {A_MONGO_TEMPLATE, "mongoTemplate"}) // spring boot needs the default "mongoTemplate" bean
public MongoTemplate aMongoTemplate(MongoConverter converter) throws ClassNotFoundException {
return new MongoTemplate(new SimpleMongoDbFactory(mongoClient, "database_a"), converter);
}
@Bean(name = B_MONGO_TEMPLATE)
public MongoTemplate bMongoTemplate(MongoConverter converter) throws ClassNotFoundException {
return new MongoTemplate(new SimpleMongoDbFactory(mongoClient, "database_b"), converter);
}
}
The problem now is that both MongoTemplate
instances create collections for all entities in their DB because indices are created at startup. So both database A and B will contain the entities handled by RepoInDbA
and RepoInDbB
(if they contain any index annotations). This happens because the c'tor of the MongoTemplate
creates a MongoPersistentEntityIndexCreator
taking a MongoMappingContext
that when setup by spring boot returns all entity classes with it's getPersistentEntities()
method (because it's a singleton bean).
To overcome this issue we created a MongoConverterWrapper
that returns a different MongoMappingContext
instance per case:
public class MongoConverterWrapper implements MongoConverter {
private final MongoConverter mongoConverter;
private final MongoMappingContext mongoMappingContext;
public MongoConverterWrapper(MongoConverter mongoConverter, Package pkg) throws ClassNotFoundException {
this.mongoConverter = mongoConverter;
// Same as org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration.mongoMappingContext
// but....
MongoMappingContext context = new MongoMappingContext();
final Set<Class<?>> initialEntitySet = new EntityScanner(applicationContext)
.scan(Document.class, Persistent.class);
// ... we remove the entity classes that don't belong to the given package.
initialEntitySet.removeIf(clazz -> !clazz.getPackage().getName().startsWith(pkg.getName()));
context.setInitialEntitySet(initialEntitySet);
Class<?> strategyClass = mongoProperties.getFieldNamingStrategy();
if (strategyClass != null) {
context.setFieldNamingStrategy(
(FieldNamingStrategy) BeanUtils.instantiate(strategyClass));
}
context.setSimpleTypeHolder(customConversions().getSimpleTypeHolder());
context.initialize();
this.mongoMappingContext = context;
}
@Override
public MongoTypeMapper getTypeMapper() {
return mongoConverter.getTypeMapper();
}
@Override
public MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> getMappingContext() {
return mongoMappingContext;
}
@Override
public ConversionService getConversionService() {
return mongoConverter.getConversionService();
}
@Override
public <R> R read(Class<R> type, DBObject source) {
return mongoConverter.read(type, source);
}
@Override
public Object convertToMongoType(Object obj) {
return mongoConverter.convertToMongoType(obj);
}
@Override
public Object convertToMongoType(Object obj, TypeInformation<?> typeInformation) {
return mongoConverter.convertToMongoType(obj, typeInformation);
}
@Override
public DBRef toDBRef(Object object, MongoPersistentProperty referingProperty) {
return mongoConverter.toDBRef(object, referingProperty);
}
@Override
public void write(Object source, DBObject sink) {
mongoConverter.write(source, sink);
}
}
So we create the MongoTemplate now like:
@Bean(name = B_MONGO_TEMPLATE)
public MongoTemplate bMongoTemplate(MongoConverter converter) throws ClassNotFoundException {
return new MongoTemplate(new SimpleMongoDbFactory(mongoClient, "database_b"), new MongoConverterWrapper(converter, RepoInDbA.class.getPackage()));
}
What could be a way to make that easier? I'm pretty sure it's a very common use case that people want to access multiple databases. Maybe I'm also missing just something?
Should e.g. spring boot automatically create a mongo template with the given name if @EnableMongoRepositories(basePackageClasses = RepoInDbB.class, mongoTemplateRef = B_MONGO_TEMPLATE)
is used somewhere so that the created MongoTemplate only handles entities in its base package?
Should you somehow make it easier to tell the MongoTemplate to only handle entities in a certain package?
No further details from DATAMONGO-1924