数据库
首页 > 数据库> > MongoDB多数据源实现

MongoDB多数据源实现

作者:互联网

源代码https://gitee.com/mr_wenpan/basis-enhance

使用参考TestMongoMutilSourceController

一、功能介绍

二、使用教程

1、基础配置

①、下载项目并打包到自己的maven仓库

下载源码,然后mvn install到自己的maven仓库。

②、项目中引入插件依赖

<dependency>
    <groupId>org.basis.enhance</groupId>
    <artifactId>enhance-boot-mongo</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

③、application.yml配置

spring:
  data:
    mongodb:
      # 默认数据源
      uri: ${MONGODB_DEFAULT_URL:mongodb://用户名:密码@ip:端口/库名}
      # 开启多数据源分片
      enable-sharding: true
      # 多数据源配置
      datasource:
        datasource1:
          order: 1
          uri: ${MONGODB_DEFAULT_URL:mongodb://用户名:密码@ip:端口/库名}
        datasource2:
          order: 2
          uri: ${MONGODB_DEFAULT_URL:mongodb://用户名:密码@ip:端口/库名}

④、启动类上开启MongoDB多数据源

// 使用@EnableMongoMultiSource注解开启MongoDB多数据源
@EnableMongoMultiSource
@SpringBootApplication
public class EnhanceMongoDemoApplication {

    public static void main(String... args) {
        SpringApplication.run(EnhanceMongoDemoApplication.class, args);
    }
}

2、使用

// MongoDB多数据源使用示例
public void multiSourceExample() {
    // 通过一致性hash算法查找对应数据源的MongoTemplate
    MongoTemplate templateByHash = mongoMultiSourceClient.getMongoTemplateByHash("wenpan");
    // 获取默认的MongoDB数据源
    MongoTemplate defaultMongoTemplate = mongoMultiSourceClient.getDefaultMongoTemplate();
    // 通过数据源名称获取对应的MongoDB数据源
    MongoTemplate datasource1MongoTemplate = mongoMultiSourceClient.getMongoTemplate("datasource1MongoTemplate");
    // 通过集合 + 分片key 获取对应的MongoDB数据源
    MongoTemplate mongoTemplate = mongoMultiSourceClient.getMongoTemplate("enhance_delivery_confirm", "xxx");

    // 使用对应数据源的MongoTemplate去操作MongoDB
    // 省略......
}

三、核心实现流程

四、问题难点

1、对于如何获取application.yml配置文件中使用方动态配置的数据源信息(比如使用方可以配置3个数据源,也可以是4个数据源,也可以是五个数据源等等),并为这些数据源创建对应的可用连接,并创建出对应的MongoTemplate然后注入到容器这是一个难题

五、核心实现

1、多数据源注入

public final class MonogoMultiDataSourceRegistrar implements EnvironmentAware, ImportBeanDefinitionRegistrar {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private Environment environment;

    @Override
    public void setEnvironment(@NonNull Environment environment) {
        this.environment = environment;
    }

    /**
     * 为每个mongo数据源注入BeanDefinition
     */
    @Override
    public void registerBeanDefinitions(@NonNull AnnotationMetadata importingClassMetadata,
                                        @NonNull BeanDefinitionRegistry registry,
                                        BeanNameGenerator importBeanNameGenerator) {
        Set<String> names = EnvironmentUtil.loadMongoDataSourceName((AbstractEnvironment) environment);
        if (names.size() <= 0) {
            logger.error("no mongo multi datasource config, inject multi datasource failed. please check config.");
            return;
        }

        logger.info("register mongo datasource: {}", names);

        for (String name : names) {
            registerMongoTemplateBeanDefinition(name, MongoTemplateFactoryBean.class, registry);
        }
    }

    /**
     * 注册 MongoTemplate BeanDefinition
     */
    protected final void registerMongoTemplateBeanDefinition(String alias, Class<?> type, BeanDefinitionRegistry registry) {
        // BeanDefinition构建器
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(type);
        // 设置通过名称注入
        builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_NAME);
        builder.addConstructorArgValue(null);
        builder.addPropertyValue(MongoDataSourceContext.FIELD_DATASOURCE_NAME, alias);

        BeanDefinition beanDefinition = builder.getBeanDefinition();
        beanDefinition.setPrimary(false);

        String beanName = alias + EnhanceMongoConstant.MultiSource.MONGO_TEMPLATE;
        BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, beanName, new String[]{alias + "-template"});
        BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
    }

    /**
     * 创建 MongoTemplate 的 FactoryBean
     * FactoryBean一般用于构建复杂的bean
     */
    protected final class MongoTemplateFactoryBean extends MongoDataSourceContext implements FactoryBean<Object> {

        private final Logger logger = LoggerFactory.getLogger(getClass());

        /**
         * 返回要创建的bean对象
         */
        @Override
        public Object getObject() throws Exception {
            // 为该数据源创建一个Mongo连接工厂,连向指定的数据源
            DynamicMongoTemplateFactory dynamicMongoTemplateFactory = getDynamicMongoTemplateFactory();

            logger.info("Dynamic create a MongoTemplate named {}", getDataSourceName());

            MongoTemplate mongoTemplate = dynamicMongoTemplateFactory.createMongoTemplate();
            // 由于这里没有注入spring容器,需要手动设置上applicationContext
            mongoTemplate.setApplicationContext(applicationContext);
            return mongoTemplate;
        }

        @Override
        public Class<?> getObjectType() {
            return MongoTemplate.class;
        }
    }

}

2、mongo连接工厂创建

public DynamicMongoTemplateFactory getDynamicMongoTemplateFactory() {
    MongoProperties mongoProperties = getMongoProperties(dataSourceName);
    MongoClientSettingsBuilderCustomizer builderCustomizers = MongoClientCreator.createMongoPropertiesCustomizer(mongoProperties, environment);
    MongoClientSettings mongoClientSettings = MongoClientCreator.createMongoClientSettings();
    MongoClient mongoClient = MongoClientCreator.createMongoClient(Collections.singletonList(builderCustomizers), mongoClientSettings);
    return new DynamicMongoTemplateFactory(mongoClient, mongoProperties, applicationContext);
}

3、mongo数据源对应的mongoTemplate创建

public MongoTemplate createMongoTemplate() throws ClassNotFoundException {
        // 创建mongo客户端工厂
        SimpleMongoClientDatabaseFactory factory = new SimpleMongoClientDatabaseFactory(mongoClient, properties.getMongoClientDatabase());
        MappingMongoConverter mappingMongoConverter = createMappingMongoConverter(factory);
        // 根据工厂创建template
        return new MongoTemplate(factory, mappingMongoConverter);
}

标签:mongo,实现,数据源,MongoTemplate,MongoDB,mongoTemplate,public
来源: https://blog.csdn.net/Hellowenpan/article/details/122185826