我们在之前的文章中已经学习了如何在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 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。
有一点需要特别注意的是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";
}
}
详细报错如下:
***************************
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})
详细报错如下:
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,另一个不能含有该注释。