You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 05:54:55