1. Introduction
1.绪论
Sometimes we need to connect to more than one database technology in the same application.
有时我们需要在同一个应用程序中连接到一个以上的数据库技术。
In this tutorial, we’ll explore the various configuration options when it comes to using multiple Spring Data modules in the same application.
在本教程中,我们将探讨在同一应用程序中使用多个Spring Data模块时的各种配置选项。
Let’s use a toy Spring Boot book store to explore the topic.
让我们用一个玩具Spring Boot书库来探讨这个话题。
2. Required Dependencies
2.所需的依赖性
First, we need to add our dependencies in the pom.xml file, so that we can use the spring-boot-starter-data-mongodb and spring-boot-starter-data-cassandra Spring Boot Data bindings:
首先,我们需要在 pom.xml 文件中添加我们的依赖项,这样我们就可以使用spring-boot-starter-data-mongodb和spring-boot-starter-data-cassandra Spring Boot Data 绑定。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-cassandra</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
3. Database Set-up
3.数据库的建立
Next, we need to set up the actual databases by using pre-built Docker images of Cassandra and Mongo:
接下来,我们需要通过使用预先构建的Docker图像的Cassandra和Mongo来设置实际数据库:。
$ docker run --name mongo-db -d -p 27017:27017 mongo:latest
$ docker run --name cassandra-db -d -p 9042:9042 cassandra:latest
These two commands will automatically download the latest Cassandra and MongoDB Docker images and run the actual containers.
这两个命令将自动下载最新的Cassandra和MongoDB Docker镜像并运行实际的容器。
We also need to forward the ports (with the -p option) from inside the containers in the actual OS environment, so that our application can access the databases.
我们还需要从实际操作系统环境中的容器内部转发端口(使用-p选项),以便我们的应用程序能够访问数据库。
We must create the database structure for Cassandra using the cqlsh utility. Keyspace creation cannot be done automatically by CassandraDataAutoConfiguration, so we need to declare it using CQL syntax.
我们必须使用cqlsh工具为 Cassandra 创建数据库结构。密钥空间的创建不能由CassandraDataAutoConfiguration自动完成,所以我们需要使用CQL语法来声明。
So first, we attach to the bash shell of the Cassandra container:
所以首先,我们附加到Cassandra容器的bashshell。
$ docker exec -it cassandra-db /bin/bash
root@419acd18891e:/# cqlsh
Connected to Test Cluster at 127.0.0.1:9042.
[cqlsh 5.0.1 | Cassandra 3.11.4 | CQL spec 3.4.4 | Native protocol v4]
Use HELP for help.
cqlsh> CREATE KEYSPACE IF NOT exists baeldung
WITH replication = {'class':'SimpleStrategy', 'replication_factor':1};
cqlsh> USE baeldung;
cqlsh> CREATE TABLE bookaudit(
bookid VARCHAR,
rentalrecno VARCHAR,
loandate VARCHAR,
loaner VARCHAR,
primary key(bookid, rentalrecno)
);
At lines 6 and 9 we then create the relevant keyspace and table.
在第6行和第9行,我们将创建相关的键空间和表.。
We could skip creating the table and simply rely on spring-boot-starter-data-cassandra to initialize the schema for us, however, since we want to explore the framework configurations individually, this is a necessary step.
我们可以跳过创建表,仅仅依靠spring-boot-starter-data-cassandra来为我们初始化模式,然而,由于我们想单独探索框架配置,这是一个必要的步骤。
By default, Mongo does not impose any kind of schema validation, so there is no need to configure anything else for it.
默认情况下,Mongo不强加任何形式的模式验证,因此不需要为其配置任何其他东西。
Finally, we configure the relevant information about the databases in our application.properties:
最后,我们在application.properties中配置数据库的相关信息。
spring.data.cassandra.username=cassandra
spring.data.cassandra.password=cassandra
spring.data.cassandra.keyspaceName=baeldung
spring.data.cassandra.contactPoints=localhost
spring.data.cassandra.port=9042
spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017
spring.data.mongodb.database=baeldung
4. Datastore Detection Mechanisms
4.数据存储检测机制
When multiple Spring Data modules are detected on the classpath, the Spring framework enters into strict repository configuration mode. This means it uses different detection mechanisms underneath, to identify which repository belongs to which persistence technology.
当在classpath上检测到多个Spring Data模块时,Spring框架会进入严格的资源库配置模式。这意味着它在下面使用不同的检测机制,以识别哪个资源库属于哪个持久化技术。
4.1. Extending Module-Specific Repository Interfaces
4.1.扩展特定模块的存储库接口
The first mechanism tries to determine if a repository extends a Spring Data module-specific repository type:
第一个机制试图确定一个存储库是否扩展了Spring Data模块特定的存储库类型。
public interface BookAuditRepository extends CassandraRepository<BookAudit, String> {
}
For the purpose of our example, the BookAudit.java contains some basic storage structure for tracking users that loaned a book:
就我们的例子而言,BookAudit.java包含了一些基本的存储结构,用于跟踪借出书籍的用户。
public class BookAudit {
private String bookId;
private String rentalRecNo;
private String loaner;
private String loanDate;
// standard getters and setters
}
The same applies to the MongoDB related repository definition:
这同样适用于MongoDB相关的资源库定义。
public interface BookDocumentRepository extends MongoRepository<BookDocument, String> {
}
This one stores the contents of the book and some relevant metadata about it:
这个存储了书的内容和关于它的一些相关元数据。
public class BookDocument {
private String bookId;
private String bookName;
private String bookAuthor;
private String content;
// standard getters and setters
}
When the application context is loaded, the framework will match each repository type using the base class it is derived from:
当应用上下文被加载时,框架将使用它派生的基类来匹配每个资源库类型。
@Test
public void givenBookAudit_whenPersistWithBookAuditRepository_thenSuccess() {
// given
BookAudit bookAudit =
new BookAudit("lorem", "ipsum", "Baeldung", "19:30/20.08.2017");
// when
bookAuditRepository.save(bookAudit);
// then
List<BookAudit> result = bookAuditRepository.findAll();
assertThat(result.isEmpty(), is(false));
assertThat(result.contains(bookAudit), is(true));
}
You can notice our domain classes are simple Java objects. In this particular situation, the Cassandra database schema must be created externally with CQL as we’ve done in section 3.
你可以注意到我们的域类是简单的Java对象。在这种特殊情况下,必须用CQL在外部创建Cassandra数据库模式,正如我们在第3节所做的那样。
4.2. Using Module-Specific Annotations on the Domain Objects
4.2.在领域对象上使用特定模块的注解
The second strategy determines the persistence technology based on module-specific annotations on the domain objects.
第二种策略根据领域对象上的特定模块注解来确定持久化技术。
Let us extend a generic CrudRepostitory and rely now on the managed objects annotations for detection:
让我们扩展一个通用的CrudRepostitory,现在依靠管理对象注解进行检测。
public interface BookAuditCrudRepository extends CrudRepository<BookAudit, String> {
}
public interface BookDocumentCrudRepository extends CrudRepository<BookDocument, String> {
}
The BookAudit.java now becomes annotated with the Cassandra specific @Table and requires a compound primary key:
BookAudit.java现在变得与Cassandra特定的@Table注释,并需要一个复合主键。
@Table
public class BookAudit {
@PrimaryKeyColumn(type = PrimaryKeyType.PARTITIONED)
private String bookId;
@PrimaryKeyColumn
private String rentalRecNo;
private String loaner;
private String loanDate;
// standard getters and setters
}
We choose the combination of bookId and rentalRecNo as our unique criteria since our application simply logs a new rental record each time somebody loans a book.
我们选择bookId和rentalRecNo的组合作为我们的唯一标准,因为我们的应用程序只是在每次有人借书时记录一个新的租赁记录。
For the BookDocument.java we use the @Document annotation which is MongoDB specific:
对于BookDocument.java,我们使用@Documentannotation,这是MongoDB特有的。
@Document
public class BookDocument {
private String bookId;
private String bookName;
private String bookAuthor;
private String content;
// standard getters and setters
}
Triggering a BookDocument save with the CrudRepository is still successful, but the returned type on line 11 is now an Iterable instead of a List:
用CrudRepository触发BookDocumentsave仍然成功,但是第11行的返回类型现在是Iterable,而不是List。
@Test
public void givenBookAudit_whenPersistWithBookDocumentCrudRepository_thenSuccess() {
// given
BookDocument bookDocument =
new BookDocument("lorem", "Foundation", "Isaac Asimov", "Once upon a time ...");
// when
bookDocumentCrudRepository.save(bookDocument);
// then
Iterable<BookDocument> resultIterable = bookDocumentCrudRepository.findAll();
List<BookDocument> result = StreamSupport.stream(resultIterable.spliterator(), false)
.collect(Collectors.toList());
assertThat(result.isEmpty(), is(false));
assertThat(result.contains(bookDocument), is(true));
}
4.3. Using Package-Based Scoping
4.3.使用基于包的范围界定
Finally, we can specify the base packages where our repositories are defined, by using the @EnableCassandraRepositories and @EnableMongoRepositories annotations:
最后,我们可以通过使用@EnableCassandraRepositories和@EnableMongoRepositories注解来指定定义存储库的基础包。
@EnableCassandraRepositories(basePackages="com.baeldung.multipledatamodules.cassandra")
@EnableMongoRepositories(basePackages="com.baeldung.multipledatamodules.mongo")
public class SpringDataMultipleModules {
public static void main(String[] args) {
SpringApplication.run(SpringDataMultipleModules.class, args);
}
}
As we can see in lines 1 and 2, this configuration mode assumes we use different packages for the Cassandra and MongoDB repositories.
正如我们在第1行和第2行看到的,这种配置模式假定我们为Cassandra和MongoDB存储库使用不同的包。
5. Conclusion
5.总结
In this tutorial, we’ve configured a simple Spring Boot application to use two different Spring Data modules in three ways.
在本教程中,我们配置了一个简单的Spring Boot应用程序,以三种方式使用两个不同的Spring Data模块。
As a first example, we extended the CassandraRepository and MongoRepository and used simple classes for the domain objects.
作为第一个例子,我们扩展了CassandraRepository和MongoRepository并为领域对象使用了简单的类。
In our second approach, we extended the generic CrudRepository interface and relied on the module-specific annotations such as @Table and @Document on our managed objects.
在我们的第二种方法中,我们扩展了通用的CrudRepository接口,并在我们的管理对象上依赖特定模块的注释,如@Table和@Document。
In the end, we used package-based detection when we configured the application with the help of @EnableCassandraRepositories and @EnableMongoRepositories.
最后,当我们在@EnableCassandraRepositories和@EnableMongoRepositories的帮助下配置应用程序时,我们使用了基于包的检测。
As always, the complete code is available over on GitHub.
一如既往,完整的代码可在GitHub上获得,。