Spring Boot Mongo多数据库问题:所有集合在各库重复创建
解决方案
问题根源:每个继承AbstractMongoClientConfiguration的配置类,默认会扫描全类路径下所有带@Document注解的实体,导致每个领域的MongoMappingContext包含了所有领域的实体定义。当MongoDB自动索引创建(或MongoTemplate初始化)时,会为这些实体在当前数据库中创建对应的空集合。
要解决这个问题,需要为每个领域的配置类明确限定只扫描当前领域的实体类,具体实现如下:
1. 为每个领域配置限定实体扫描范围
在每个领域的AbstractMongoClientConfiguration子类中,重写mongoManagedTypes方法,指定当前领域的实体所在包或具体实体类。
修改后的Domain A配置
@Configuration @EnableMongoRepositories( basePackageClasses = {DomainAMongoRepository.class}, mongoTemplateRef = "domainAMongoTemplate") @Slf4j @EnableConfigurationProperties(DomainADbMongoConfiguration.class) public class DomainADbConfig extends AbstractMongoClientConfiguration { private final DomainADbMongoConfiguration mongoConfiguration; @Autowired public DomainADbConfig(DomainADbMongoConfiguration mongoConfiguration) { this.mongoConfiguration = mongoConfiguration; } // 原有Bean方法保持不变 @Primary @Bean(name = "domainAMongoTemplate") public MongoTemplate domainAMongoTemplate( @Qualifier("domainAMongoDbFactory") MongoDatabaseFactory mongoDbFactory, @Qualifier("domainAMongoConverter") MappingMongoConverter mongoConverter ) { return new MongoTemplate(mongoDbFactory, mongoConverter); } @Primary @Bean(name = "domainAMongoDbFactory") public MongoDatabaseFactory mongoDbFactory( @Qualifier("domainAMongoClient") MongoClient mongoClient ) { return new SimpleMongoClientDatabaseFactory(mongoClient, getDatabaseName()); } @Primary @Bean(name = "domainAMongoConverter") public MappingMongoConverter mongoConverter() throws Exception { MongoMappingContext mongoMappingContext = mongoMappingContext(customConversions(), mongoManagedTypes()); mongoMappingContext.afterPropertiesSet(); MappingMongoConverter converter = new MappingMongoConverter(new DefaultDbRefResolver(mongoDbFactory()), mongoMappingContext); converter.setCustomConversions(customConversions()); converter.afterPropertiesSet(); return converter; } @Override @Bean(name = "domainAMongoClient") @Primary public MongoClient mongoClient() { return MongoClients.create( MongoClientSettings.builder() .uuidRepresentation(UuidRepresentation.JAVA_LEGACY) .applyConnectionString(new ConnectionString(mongoConfiguration.getUri())) .build()); } // 新增:限定只扫描Domain A的实体类 @Override protected MongoManagedTypes mongoManagedTypes() { // 方式1:通过实体类所在包名批量指定 return MongoManagedTypes.fromPackage(DomainAEntity.class.getPackageName()); // 方式2:直接指定单个/多个实体类(适合实体分散的场景) // return MongoManagedTypes.fromBasePackageClasses(DomainAEntity.class, AnotherDomainAEntity.class); } @Override protected String getDatabaseName() { return mongoConfiguration.getDatabase(); } } // 配置属性类保持不变 @ConfigurationProperties("domain-a.mongo") class DomainADbMongoConfiguration { private String database; private String uri; public String getDatabase() { return database; } public void setDatabase(String database) { this.database = database; } public String getUri() { return uri; } public void setUri(String uri) { this.uri = uri; } }
修改后的Domain B配置
@Configuration @EnableMongoRepositories( basePackageClasses = {DomainBTypeMongoRepository.class, DomainBMongoRepository.class}, mongoTemplateRef = "domainBMongoTemplate") @Slf4j @EnableConfigurationProperties(DomainBDbMongoConfiguration.class) public class DomainBDbConfig extends AbstractMongoClientConfiguration { private final DomainBDbMongoConfiguration mongoConfiguration; @Autowired public DomainBDbConfig(DomainBDbMongoConfiguration mongoConfiguration) { this.mongoConfiguration = mongoConfiguration; } // 原有Bean方法保持不变 @Bean(name = "domainBMongoTemplate") public MongoTemplate topicMongoTemplate( @Qualifier("domainBMongoDbFactory") MongoDatabaseFactory domainBMongoDbFactory, @Qualifier("domainBMongoConverter") MappingMongoConverter domainBMongoConverter) { log.info("Connecting to Mongo DomainBs db"); return new MongoTemplate(domainBMongoDbFactory, domainBMongoConverter); } @Bean(name = "domainBMongoDbFactory") public MongoDatabaseFactory mongoDbFactory( @Qualifier("domainBMongoClient") MongoClient mongoClient) { return new SimpleMongoClientDatabaseFactory(mongoClient, getDatabaseName()); } @Bean(name = "domainBMongoConverter") public MappingMongoConverter mongoConverter( @Qualifier("domainBMongoDbFactory") MongoDatabaseFactory domainBMongoDbFactory) throws Exception { MongoMappingContext mongoMappingContext = mongoMappingContext(customConversions(), mongoManagedTypes()); mongoMappingContext.afterPropertiesSet(); MappingMongoConverter converter = new MappingMongoConverter( new DefaultDbRefResolver(domainBMongoDbFactory), mongoMappingContext); converter.setCustomConversions(customConversions()); converter.afterPropertiesSet(); return converter; } @Override @Bean(name = "domainBMongoClient") public MongoClient mongoClient() { return MongoClients.create( MongoClientSettings.builder() .uuidRepresentation(UuidRepresentation.JAVA_LEGACY) .applyConnectionString(new ConnectionString(mongoConfiguration.getUri())) .build()); } // 新增:限定只扫描Domain B的实体类 @Override protected MongoManagedTypes mongoManagedTypes() { return MongoManagedTypes.fromPackage(DomainBEntity.class.getPackageName()); // 或指定具体实体类 // return MongoManagedTypes.fromBasePackageClasses(DomainBEntity.class, DomainBTypeEntity.class); } @Override protected boolean autoIndexCreation() { return true; } @Override protected String getDatabaseName() { return mongoConfiguration.getDatabase(); } } // 配置属性类保持不变 @ConfigurationProperties("domainBs.mongo") class DomainBDbMongoConfiguration { private String uri; private String database; public String getDatabase() { return database; } public void setDatabase(String database) { this.database = database; } public String getUri() { return uri; } public void setUri(String uri) { this.uri = uri; } }
2. 清理冗余代码
移除原有配置中手动调用的mongoMappingContext.setInitialEntitySet(getInitialEntitySet()),因为mongoManagedTypes会自动管理实体集合,避免重复扫描导致的范围溢出。
原理说明
MongoManagedTypes是Spring Data MongoDB用于定义实体管理范围的核心类,替代了旧版的getInitialEntitySet方法。- 通过为每个领域单独指定实体范围,确保每个领域的
MongoMappingContext只包含当前领域的实体定义,MongoTemplate只会为这些实体在对应数据库中创建集合。 - 即使开启了
autoIndexCreation,也只会为当前领域的实体创建索引和集合,不会再生成其他领域的空集合。
内容的提问来源于stack exchange,提问作者NRaf
相关产品推荐
相关产品推荐

