Spring Boot配置MongoDB多数据源

我们在之前的文章中已经学习了如何在Spring Boot中连接MongoDB数据库。
Spring Boot中使用MongoDB数据库
实际项目中我们可能需要连接多个MongoDB数据源,比如用户库user,日志库log。本章我们来记录连接多个数据源的步骤,以两个数据源为例,多个数据源类推。

数据源配置文件配置两个数据源

我们之前的配置是配置在application.properties文件中。
对配置文件不了解的可以参考
spring boot配置文件和修改端口
新增两个数据源如下:

spring.data.mongodb.user.database=db_user
spring.data.mongodb.user.host=192.168.30.249
spring.data.mongodb.user.port=28719
#spring.data.mongodb.user.username=username
#spring.data.mongodb.user.password=password


spring.data.mongodb.log.database=db_log
spring.data.mongodb.log.host=192.168.30.249
spring.data.mongodb.log.port=28719
#spring.data.mongodb.log.username=username
#spring.data.mongodb.log.password=password

新建一个基础的抽象类

AbstractMongoConfigure.java
内容如下:

package com.biologic.util;

import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;

import com.mongodb.MongoClient;

public abstract class AbstractMongoConfig {

    // 变量名跟配置的参数对应
    private String host, database, username, password;
    private int port;

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public String getDatabase() {
        return database;
    }

    public void setDatabase(String database) {
        this.database = database;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }


    public MongoDbFactory mongoDbFactory() throws Exception {
        // 无认证的初始化方法
        return new SimpleMongoDbFactory(new MongoClient(host, port), database);

        // 有认证的初始化方法
        // ServerAddress serverAddress = new ServerAddress(host, port);
        // List<MongoCredential> mongoCredentialList = new ArrayList<>();
        // mongoCredentialList.add(MongoCredential.createCredential(username, database,
        // password.toCharArray()));
        // return new SimpleMongoDbFactory(new MongoClient(serverAddress,
        // mongoCredentialList), database);
    }


    /*
     * Factory method to create the MongoTemplate
     */
    abstract public MongoTemplate getMongoTemplate() throws Exception;

}

或者


public abstract class AbstractMongoConfig {

private String uri;
/*
     * Method that creates MongoDbFactory Common to both of the MongoDb
     * connections
     */
    public MongoDbFactory mongoDbFactory() throws Exception {

        return new SimpleMongoDbFactory(new MongoClientURI(uri);
    }

    /*
     * Factory method to create the MongoTemplate
     */
    abstract public MongoTemplate getMongoTemplate() throws Exception;

数据源加载

数据源加载需要新建继承AbstractMongoConfigure的加载类,有多少个数据源就需要新建多少个数据源加载类。
我们这里需要新建两个数据源加载类

新建用户数据源加载类

UserMongoConfig.java

package com.biologic.util;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.mongodb.core.MongoTemplate;

//prefix需要与配置文件中用户user数据源的配置前缀对应
@Configuration  
@ConfigurationProperties(prefix = "spring.data.mongodb.user")
public class UserMongoConfig extends AbstractMongoConfig {     


     @Primary
     @Bean(name = "mongoTemplate")
     @Override
     public  MongoTemplate getMongoTemplate() throws Exception {        
         return new MongoTemplate(mongoDbFactory());    
     }


}

新建日志数据源加载类

package com.biologic.util;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.core.MongoTemplate;

//prefix需要与配置文件中日志log数据源的配置前缀对应
@Configuration  
@ConfigurationProperties(prefix = "spring.data.mongodb.log")
public class LogMongoConfig extends AbstractMongoConfig {     

     @Override   
     @Bean(name = "logMongoTemplate")
     public  MongoTemplate getMongoTemplate() throws Exception {        
         return new MongoTemplate(mongoDbFactory());    
     }


}

需要注意的是 多个数据源中有一个需要设置bean名为mongoTemplate,而且注释为@Primary,否则WebMvcConfigurationSupport.class等会报错找不到mongoTemplate。

排除mongo的自动注入

有一点需要特别注意的是Spring Boot会自动注入mongoTemplate,与我们设置的多个数据源有冲突。为了防止默认注入,需要排除自动注入的类。
找到Spring Boot的启动类Applocation.java在@SpringBootApplication后面新增排除类如下:

@SpringBootApplication(exclude = {MongoAutoConfiguration.class,MongoDataAutoConfiguration.class,MongoDbAutoConfiguration.class})

完整的Applocation.java如下:

package com.biologic;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;

import com.mofang.microservice.support.boot.feign.FeignDefaultContractConfiguration;
import com.mofang.microservice.support.boot.mongodb.MongoDbAutoConfiguration;



@SpringBootApplication(exclude = {MongoAutoConfiguration.class,MongoDataAutoConfiguration.class,MongoDbAutoConfiguration.class})
@EnableDiscoveryClient
public class Applocation {

    public static void main(String[] args) throws Exception {
        SpringApplication.run(Applocation.class, args);
    }

}

使用多个数据源

使用时,直接指明对应注入即可,如下:

package com.biologic.api;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.biologic.api.service.QualityCheckRepository;
import com.biologic.entity.QualityCheck;

@Controller
@RequestMapping(path = "/demo")
public class QualityCheckController {

    @Autowired
    @Qualifier(value = "mongoTemplate")
    MongoTemplate mongoTemplate;

    @Autowired
    @Qualifier(value = "logMongoTemplate")
    MongoTemplate logMongoTemplate;

    @GetMapping(path = "/qualityCheck")
    public @ResponseBody String qualityCheck() {


            PrepareReportPool repareReportPool = new PrepareReportPool();
            repareReportPool.setBarcodes(okBarCodes);
            repareReportPool.setChangeStateTaskId(autoQualityCheckLog.getId());
            repareReportPool.setCreateTime(new Date());
            mongoTemplate.save(repareReportPool);

        AutoQualityCheckLog autoQualityCheckLog = new AutoQualityCheckLog();
        autoQualityCheckLog.setId(UUID.randomUUID().toString());
        autoQualityCheckLog.setCheckTime(new Date());

        autoQualityCheckLog.setAbnormalBarcodes(abnormalBarCodes);
        autoQualityCheckLog.setFailBarcodes(failbarcodeList);
        autoQualityCheckLog.setPassBarcodes(okBarCodes);
        autoQualityCheckLog.setNoDpBarcodes(noDpBarcodes);
        autoQualityCheckLog.setStateAbnormalDp(stateAbnormalDp);
        autoQualityCheckLog.setOkStateChangeBarcodes(okStateChangeBarcodes);
        autoQualityCheckLog.setFailStateChangeBarcodes(failStateChangeBarcodes);
        logMongoTemplate.save(autoQualityCheckLog);
        return "ok";
    }

}

可能遇到的问题-MongoClient’ that could not be found

详细报错如下:

***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 0 of method mongoDbFactory in org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration required a bean of type 'com.mongodb.MongoClient' that could not be found.
    - Bean method 'mongo' not loaded because auto-configuration 'MongoAutoConfiguration' was excluded


Action:

Consider revisiting the conditions above or defining a bean of type 'com.mongodb.MongoClient' in your configuration.

原因
重写了MongoClient等之后导致原来的自动注入缺少bean

解决方式
主要是看哪个自动注入的类在引用默认的MongoClient,把它排除出去即可,例如:

@SpringBootApplication(exclude = {MongoAutoConfiguration.class,MongoDataAutoConfiguration.class,MongoDbAutoConfiguration.class})

可能遇到的问题-more than one ‘primary’ bean found among candidates

详细报错如下:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'sampleController': Unsatisfied dependency expressed through field 'mongoTemplate'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.springframework.data.mongodb.core.MongoTemplate' available: more than one 'primary' bean found among candidates: [logMongoTemplate, userMongoTemplate, mongoTemplate]
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:588) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:366) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1264) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:866) ~[spring-context-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:542) ~[spring-context-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122) ~[spring-boot-1.5.2.RELEASE.jar:1.5.2.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:737) [spring-boot-1.5.2.RELEASE.jar:1.5.2.RELEASE]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:370) [spring-boot-1.5.2.RELEASE.jar:1.5.2.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:314) [spring-boot-1.5.2.RELEASE.jar:1.5.2.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1162) [spring-boot-1.5.2.RELEASE.jar:1.5.2.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1151) [spring-boot-1.5.2.RELEASE.jar:1.5.2.RELEASE]
    at com.biologic.Applocation.main(Applocation.java:18) [classes/:na]
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.springframework.data.mongodb.core.MongoTemplate' available: more than one 'primary' bean found among candidates: [logMongoTemplate, userMongoTemplate, mongoTemplate]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.determinePrimaryCandidate(DefaultListableBeanFactory.java:1365) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.determineAutowireCandidate(DefaultListableBeanFactory.java:1326) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1113) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:585) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE]

原因
Spring Boot会自动注入一个默认的mongoTemplate或者我们设置了多个@Primary数据源。

解决方式
排除Spring Boot自动注入的类,我们自动重写的mongoTemplate需要且只能设置一个为@Primary。
参考

@SpringBootApplication(exclude = {MongoAutoConfiguration.class,MongoDataAutoConfiguration.class,MongoDbAutoConfiguration.class})

和数据源加载小节,一个数据源注释@Primary,另一个不能含有该注释。

猜你喜欢

转载自blog.csdn.net/q383965374/article/details/81103917