1. Overview
1.概述
Using Spring Data MongoDB, we can create a MongoClient to do operations against the database. However, sometimes, we might need to use multiple databases in our applications.
使用Spring Data MongoDB,我们可以创建一个MongoClient来对数据库进行操作。然而,有时我们可能需要在我们的应用程序中使用多个数据库。
In this tutorial, we’ll create multiple connections to MongoDB. We’ll also add some Spring Boot tests to mock this scenario.
在本教程中,我们将创建多个连接到MongoDB。我们还将添加一些Spring Boot测试来模拟这一场景。
2. Multiple Databases Application With Spring Data MongoDB
2.使用Spring Data MongoDB的多数据库应用
When using MongoDB, we create a MongoTemplate to access data. So, we could create multiple templates to connect to various databases.
在使用MongoDB时,我们创建一个MongoTemplate来访问数据。因此,我们可以创建多个模板以连接到各种数据库。
However, we can get the NoUniqueBeanDefinitionException because Spring doesn’t find a unique bean.
然而,我们可以得到NoUniqueBeanDefinitionException,因为Spring没有找到一个独特的Bean。。
With this in mind, let’s see how we can build a Spring Boot configuration.
考虑到这一点,让我们看看如何建立一个Spring Boot配置。
2.1. Dependency Setup
2.1.依赖关系的设置
Let’s start by adding dependencies to the pom.xml. First, we need a spring boot starter:
首先,我们需要一个spring boot starter:。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.4</version>
<relativePath />
</parent>
Then, we need dependencies for a web starter and data 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>
Similarly, if we use Gradle, we add to the build.gradle:
同样地,如果我们使用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'
As an alternative, we can use Spring Initializer.
作为一个替代方案,我们可以使用Spring Initializer。
2.2. Model
2.2 模式
First, let’s add our model. We’ll create two documents that will be in use by two different databases.
首先,让我们添加我们的模型。我们将创建两个文件,它们将被两个不同的数据库使用。
For example, we’ll create a User document:
例如,我们将创建一个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
}
Then, let’s also add an Account document:
然后,让我们也添加一个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. Repository
2.3.存储库
Then, we create a repository for each model class with some Spring Data methods.
然后,我们为每个模型类创建一个带有一些Spring Data方法的资源库。
First, let’s add a UserRepository:
首先,让我们添加一个UserRepository。
@Repository
public interface UserRepository extends MongoRepository<User, String> {
User findByEmail(String email);
}
Following, we add an AccountRepository:
接下来,我们添加一个AccountRepository。
@Repository
public interface AccountRepository extends MongoRepository<Account, String> {
Account findByAccountDomain(String account);
}
2.4. Connection Properties
2.4.连接属性
Let’s define the properties for the multiple databases we’re using:
让我们为我们正在使用的多个数据库定义属性。
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
It’s worth noticing that we have a property for the specific DB we’ll use for authentication.
值得注意的是,我们有一个属性用于我们将用于认证的特定DB。
2.5. Primary Configuration
2.5.主要配置
Now, we need our configuration. We’ll make one for each database.
现在,我们需要我们的配置。我们将为每个数据库做一个配置。
Let’s have a look at our primary class definition in use for the UserRepository.:
让我们看看我们的主类定义在使用UserRepository。
@Configuration
@EnableMongoRepositories(basePackageClasses = UserRepository.class, mongoTemplateRef = "primaryMongoTemplate")
@EnableConfigurationProperties
public class PrimaryConfig {
// beans
}
To demonstrate, let’s break down all the beans and annotations.
为了演示,让我们把所有的Bean和注解分解开来。
Firstly, we’ll retrieve and set properties using MongoProperties. This way, we directly map all the properties to a bean:
首先,我们将使用MongoProperties来检索和设置属性。这样,我们直接将所有属性映射到Bean上。
@Bean(name = "primaryProperties")
@ConfigurationProperties(prefix = "mongodb.primary")
@Primary
public MongoProperties primaryProperties() {
return new MongoProperties();
}
To grant access to multiple users, we use a MongoDB authentication mechanism with MongoCredential. We construct our credential object by adding an authentication database, admin in this case:
为了向多个用户授予访问权,我们使用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());
}
As suggested by the latest releases, we’re using SimpleMongoClientDatabaseFactory instead of creating a MongoTemplate from a connection string:
根据最新版本的建议,我们使用SimpleMongoClientDatabaseFactory,而不是从连接字符串中创建MongoTemplate:。
@Primary
@Bean(name = "primaryMongoDBFactory")
public MongoDatabaseFactory mongoDatabaseFactory(
@Qualifier("primaryMongoClient") MongoClient mongoClient,
@Qualifier("primaryProperties") MongoProperties mongoProperties) {
return new SimpleMongoClientDatabaseFactory(mongoClient, mongoProperties.getDatabase());
}
We need our beans to be @Primary here as we’ll add more database configurations. Otherwise, we’ll fall under the uniqueness constraint we discussed earlier.
我们需要我们的Bean在这里成为@Primary,因为我们将添加更多的数据库配置。否则,我们就会陷入我们之前讨论的唯一性约束中。
We do the same with JPA when we are mapping multiple EntityManager. Similarly, we’ll need a reference to the Mongotemplate in our @EnableMongoRepositories:
当我们在映射多个EntityManager时,我们对JPA的做法也是如此。同样地,我们需要在我们的@EnableMongoRepositories:中引用Mongotemplate。
@EnableMongoRepositories(basePackageClasses = UserRepository.class, mongoTemplateRef = "primaryMongoTemplate")
2.6. Secondary Configurations
2.6.二级配置
Finally, to double-check, let’s have a look at the second database configuration:
最后,为了仔细检查,让我们看一下第二个数据库的配置。
@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);
}
}
In this case, it will reference the AccountRepository or all the classes belonging to the same package.
在这种情况下,它将引用AccountRepository或属于同一包的所有类.。
3. Tests
3.测试
We’ll test the application against a MongoDB instance. We can use MongoDB with Docker.
我们将针对MongoDB实例测试该应用程序。我们可以使用MongoDB与Docker。
3.1. Start a MongoDB Container
3.1.启动一个MongoDB容器
Let’s run a MongoDB container using Docker Compose. Let’s have a look at our YAML template:
让我们使用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
We need to initialize with a root user if we want authentication to be in place. To populate the database with more users, we are adding a bind mount to a JavaScript init file:
如果我们希望认证到位,我们需要用一个根用户进行初始化。为了用更多的用户来填充数据库,我们要在JavaScript init文件中添加一个bind mount:。
db.createUser(
{
user: "user1",
pwd: "password",
roles: [ { role: "readWrite", db: "db1" } ]
}
)
db.createUser(
{
user: "user2",
pwd: "password",
roles: [ { role: "readWrite", db: "db2" } ]
}
)
Let’s run our container:
让我们运行我们的容器。
docker-compose up -d
When the container starts, it creates a volume for the mongo-init.js file and copies it to the container’s entry point.
当容器启动时,它会为mongo-init.js文件创建一个卷,并将其复制到容器的入口点。
3.2. Spring Boot Tests
3.2.Spring Boot测试
Let’s wrap it all together in some basic Spring Boot tests:
让我们用一些基本的Spring Boot测试将这一切包起来。
@SpringBootTest(classes = { SpringBootMultipeDbApplication.class })
@TestPropertySource("/multipledb/multidb.properties")
public class MultipleDbUnitTest {
// set up
@Test
void whenFindUserByEmail_thenNameOk() {
assertEquals("name", userRepository.findByEmail("user@gmail.com")
.getName());
}
@Test
void whenFindAccountByDomain_thenNickNameOk() {
assertEquals("nickname", accountRepository.findByAccountDomain("account@jira.baeldung.com")
.getNickName());
}
}
Primarily, we want to establish a connection for databases and get authentication. If we don’t, we might have the database not populated or the MongoDb instance not started.
主要是,我们要为数据库建立连接并获得认证。如果我们不这样做,我们可能会出现数据库没有填充或MongoDb实例没有启动。
In those cases, we can have a look at the logs of our DB container, for example:
在这些情况下,我们可以看一下我们的DB容器的日志,比如说。
docker logs 30725c8635d4
It’s worth noticing that the initial JavaScript executes only the first time the container runs. So, if we need to run with a different script, we may need to delete the volume:
值得注意的是,初始JavaScript只在容器第一次运行时执行。所以,如果我们需要用不同的脚本来运行,我们可能需要删除这个卷。
docker-compose down -v
4. Conclusion
4.总结
In this article, we’ve seen how to create multiple connections with Spring Data MongoDB. We saw how to add credentials to get authenticated. Most importantly, we’ve seen how to create configurations, one of which must be with primary beans. Finally, we’ve added some test cases against a running MongoDB instance using Docker and Spring Boot testing,
在这篇文章中,我们已经看到了如何用Spring Data MongoDB创建多个连接。我们看到了如何添加凭证以获得认证。最重要的是,我们看到了如何创建配置,其中一个配置必须是主Bean。最后,我们使用Docker和Spring Boot测试,针对运行中的MongoDB实例添加了一些测试案例。
As always, the entire code of the article is available over on GitHub.
一如既往,该文章的全部代码可在GitHub上获得。