1. 概述
使用Spring Data MongoDB,我们可以创建一个MongoClient来对数据库进行操作。然而,在某些应用中,我们可能需要连接多个数据库。
在这个教程中,我们将创建多个MongoDB连接,并添加一些Spring Boot测试来模拟这种场景。
2. 使用Spring Data MongoDB的多数据库应用程序
在使用MongoDB时,我们创建一个MongoTemplate来访问数据。因此,我们可以为不同的数据库创建多个模板。
但是,我们可能会遇到NoUniqueBeanDefinitionException问题,因为Spring找不到唯一的bean。
考虑到这一点,让我们看看如何构建Spring Boot配置。
2.1. 依赖设置
首先,我们在pom.xml中添加依赖。我们需要一个spring boot starter:*
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<relativePath />
</parent>
接着,我们需要Web启动器和MongoDB数据启动器的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
同样,如果使用Gradle,我们在build.gradle中添加:
plugins {
id 'org.springframework.boot' version '2.6.4'
}
compile 'org.springframework.boot:spring-boot-starter-data-mongodb'
compile 'org.springframework.boot:spring-boot-starter-web'
或者,我们可以使用Spring Initializer。
2.2. 模型
首先,我们添加模型。我们将创建两个文档,分别用于两个不同的数据库。
例如,我们将创建一个User文档:
@Document(collection = "user")
public class User {
@MongoId
private ObjectId id;
private String name;
private String surname;
private String email;
private int age;
// getters and setters
}
然后,我们也添加一个Account文档:
@Document(collection = "account")
public class Account {
@MongoId
private ObjectId id;
private String userEmail;
private String nickName;
private String accountDomain;
private String password;
// getters and setters
}
2.3. 仓库
然后,我们为每个模型类创建一个仓库,包含一些Spring Data方法。
首先,我们添加一个UserRepository:
@Repository
public interface UserRepository extends MongoRepository<User, String> {
User findByEmail(String email);
}
接下来,我们添加一个AccountRepository:
@Repository
public interface AccountRepository extends MongoRepository<Account, String> {
Account findByAccountDomain(String account);
}
2.4. 连接属性
现在,我们定义用于多个数据库的属性:
mongodb.primary.host=localhost
mongodb.primary.database=db1
mongodb.primary.authenticationDatabase=admin
mongodb.primary.username=user1
mongodb.primary.password=password
mongodb.primary.port=27017
mongodb.secondary.host=localhost
mongodb.secondary.database=db2
mongodb.secondary.authenticationDatabase=admin
mongodb.secondary.username=user2
mongodb.secondary.password=password
mongodb.secondary.port=27017
值得注意的是,我们有一个特定于使用的数据库的属性,例如用于身份验证。
2.5. 主要配置
现在,我们需要我们的配置。我们将为每个数据库创建一个。
让我们看看使用UserRepository的主配置类定义:
@Configuration
@EnableMongoRepositories(basePackageClasses = UserRepository.class, mongoTemplateRef = "primaryMongoTemplate")
@EnableConfigurationProperties
public class PrimaryConfig {
// beans
}
为了演示,让我们分解所有的bean和注解。
首先,我们将使用*MongoProperties*获取并设置属性。这样,我们可以直接将所有属性映射到一个bean上:
@Bean(name = "primaryProperties")
@ConfigurationProperties(prefix = "mongodb.primary")
@Primary
public MongoProperties primaryProperties() {
return new MongoProperties();
}
为了允许多个用户访问,我们使用MongoDB的身份验证机制与*MongoCredential。我们通过添加认证数据库(在这种情况下是admin*)构造我们的凭证对象:
@Bean(name = "primaryMongoClient")
public MongoClient mongoClient(@Qualifier("primaryProperties") MongoProperties mongoProperties) {
MongoCredential credential = MongoCredential
.createCredential(mongoProperties.getUsername(), mongoProperties.getAuthenticationDatabase(), mongoProperties.getPassword());
return MongoClients.create(MongoClientSettings.builder()
.applyToClusterSettings(builder -> builder
.hosts(singletonList(new ServerAddress(mongoProperties.getHost(), mongoProperties.getPort()))))
.credential(credential)
.build());
}
根据最新版本的建议,我们使用SimpleMongoClientDatabaseFactory而不是使用连接字符串创建MongoTemplate:
@Primary
@Bean(name = "primaryMongoDBFactory")
public MongoDatabaseFactory mongoDatabaseFactory(
@Qualifier("primaryMongoClient") MongoClient mongoClient,
@Qualifier("primaryProperties") MongoProperties mongoProperties) {
return new SimpleMongoClientDatabaseFactory(mongoClient, mongoProperties.getDatabase());
}
在这里,我们需要将我们的bean标记为*@Primary*,因为我们将在后面添加更多的数据库配置。否则,我们将陷入之前讨论的独特性约束。
当我们映射多个EntityManager到JPA时,我们也需要在EnableMongoRepositories中引用Mongotemplate:
@EnableMongoRepositories(basePackageClasses = UserRepository.class, mongoTemplateRef = "primaryMongoTemplate")
2.6. 二级配置
最后,为了确认,让我们看看第二个数据库配置:
@Configuration
@EnableMongoRepositories(basePackageClasses = AccountRepository.class, mongoTemplateRef = "secondaryMongoTemplate")
@EnableConfigurationProperties
public class SecondaryConfig {
@Bean(name = "secondaryProperties")
@ConfigurationProperties(prefix = "mongodb.secondary")
public MongoProperties secondaryProperties() {
return new MongoProperties();
}
@Bean(name = "secondaryMongoClient")
public MongoClient mongoClient(@Qualifier("secondaryProperties") MongoProperties mongoProperties) {
MongoCredential credential = MongoCredential
.createCredential(mongoProperties.getUsername(), mongoProperties.getAuthenticationDatabase(), mongoProperties.getPassword());
return MongoClients.create(MongoClientSettings.builder()
.applyToClusterSettings(builder -> builder
.hosts(singletonList(new ServerAddress(mongoProperties.getHost(), mongodProperties.getPort()))))
.credential(credential)
.build());
}
@Bean(name = "secondaryMongoDBFactory")
public MongoDatabaseFactory mongoDatabaseFactory(
@Qualifier("secondaryMongoClient") MongoClient mongoClient,
@Qualifier("secondaryProperties") MongoProperties mongoProperties) {
return new SimpleMongoClientDatabaseFactory(mongoClient, mongoProperties.getDatabase());
}
@Bean(name = "secondaryMongoTemplate")
public MongoTemplate mongoTemplate(@Qualifier("secondaryMongoDBFactory") MongoDatabaseFactory mongoDatabaseFactory) {
return new MongoTemplate(mongoDatabaseFactory);
}
}
在这种情况下,它将引用AccountRepository或同一包下的所有类。
3. 测试
我们将针对MongoDB实例测试应用程序。我们可以使用Docker中的MongoDB。
3.1. 启动MongoDB容器
让我们使用Docker Compose运行一个MongoDB容器。让我们看看我们的YAML模板:
services:
mongo:
hostname: localhost
container_name: 'mongo'
image: 'mongo:latest'
expose:
- 27017
ports:
- 27017:27017
environment:
- MONGO_INITDB_DATABASE=admin
- MONGO_INITDB_ROOT_USERNAME=admin
- MONGO_INITDB_ROOT_PASSWORD=admin
volumes:
- ./mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js
如果我们想要启用身份验证,我们需要使用root用户初始化。如果需要在数据库中填充更多用户,我们添加了一个bind mount到JavaScript初始化文件:
db.createUser(
{
user: "user1",
pwd: "password",
roles: [ { role: "readWrite", db: "db1" } ]
}
)
db.createUser(
{
user: "user2",
pwd: "password",
roles: [ { role: "readWrite", db: "db2" } ]
}
)
运行我们的容器:
docker-compose up -d
当容器启动时,它为mongo-init.js文件创建一个卷并将其复制到容器的入口点。
3.2. Spring Boot测试
现在,让我们将所有内容汇总到基本的Spring Boot测试中:
@SpringBootTest(classes = { SpringBootMultipeDbApplication.class })
@TestPropertySource("/multipledb/multidb.properties")
public class MultipleDbUnitTest {
// set up
@Test
void whenFindUserByEmail_thenNameOk() {
assertEquals("name", userRepository.findByEmail("[email protected]")
.getName());
}
@Test
void whenFindAccountByDomain_thenNickNameOk() {
assertEquals("nickname", accountRepository.findByAccountDomain("[email protected]")
.getNickName());
}
}
主要目标是建立数据库连接并进行身份验证。如果没有,我们可能会发现数据库未填充或MongoDb实例未启动。
在这种情况下,我们可以查看数据库容器的日志,例如:
docker logs 30725c8635d4
值得注意的是,初始的JavaScript脚本只在容器首次运行时执行。因此,如果我们需要使用不同的脚本,我们可能需要删除卷:
docker-compose down -v
4. 总结
在这篇文章中,我们了解了如何使用Spring Data MongoDB创建多个连接。我们看到了如何添加凭证以进行身份验证。最重要的是,我们看到了如何创建配置,其中一个必须是主bean。最后,我们添加了一些针对运行中的MongoDB实例的测试案例,使用Docker和Spring Boot测试。
如往常一样,本文的所有代码可在GitHub上找到。