1. 引言
有時,我們需要在同一個應用程序中連接到多種數據庫技術。
在本教程中,我們將探討在同一應用程序中使用多個 Spring Data 模塊時的各種配置選項。
讓我們使用一個簡單的 Spring Boot 書店來探索這個主題。
2. 所需依賴
首先,我們需要在 pom.xml 文件中添加我們的依賴,以便我們可以使用 Spring Boot Data 的 spring-boot-starter-data-mongodb 和 spring-boot-starter-data-cassandra 綁定。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-cassandra</artifactId>
<version>3.1.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
<version>3.1.5</version>
</dependency>3. 數據庫設置
接下來,我們需要通過使用預構建的 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
這兩個命令將自動下載最新的 Cassandra 和 MongoDB Docker 鏡像並運行實際容器。
此外,還需要通過 -p 選項將端口轉發到實際操作系統環境中的容器內部,以便我們的應用程序可以訪問數據庫。
必須使用 cqlsh 工具創建 Cassandra 的數據庫結構。CassandraDataAutoConfiguration 不能自動創建 Keyspace,因此我們需要使用 CQL 語法進行聲明。
首先,我們需要連接到 Cassandra 容器的 bash shell:
$ 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)
);
在第6行和第9行,我們創建了相關的 keyspace 和表。
我們可以跳過創建表的步驟,僅僅依賴 spring-boot-starter-data-cassandra 來為我們初始化模式,但由於我們希望分別探索框架配置,因此這是一個必要的步驟。
默認情況下,MongoDB 不會對模式進行任何驗證,因此不需要進行任何其他配置。
最後,我們在 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=baeldung4. 數據存儲檢測機制
當 classpath 上檢測到多個 Spring Data 模塊時,Spring 框架將進入嚴格的倉庫配置模式。這意味着它會使用不同的檢測機制,以確定哪個倉庫屬於哪種持久化技術。
4.1. 擴展模塊特定倉庫接口
以下機制嘗試確定一個倉庫是否擴展了 Spring Data 模塊特定倉庫類型:
public interface BookAuditRepository extends CassandraRepository<BookAudit, String> {
}為了説明目的,BookAudit.java 包含了一些基本的存儲結構,用於跟蹤借閲書籍的用户:
public class BookAudit {
private String bookId;
private String rentalRecNo;
private String loaner;
private String loanDate;
// standard getters and setters
}
以下是翻譯後的內容:
同樣適用於 MongoDB 相關倉庫的定義:
public interface BookDocumentRepository extends MongoRepository<BookDocument, String> {
}此項存儲書籍的內容以及與其相關的元數據。
public class BookDocument {
private String bookId;
private String bookName;
private String bookAuthor;
private String content;
// standard getters and setters
}當應用程序上下文加載時,框架將使用其派生出的基本類來匹配每個存儲庫類型:
@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));
}你可以注意到我們的領域類是簡單的Java對象。在這種特定情況下,Cassandra數據庫模式必須由外部創建,使用CQL,正如我們在第3節中所做的那樣。
4.2. 使用領域對象模塊特定註解
第二種策略通過對領域對象上的模塊特定註解來確定持久化技術。
讓我們擴展一個通用的CrudRepostitory,並現在依賴於管理對象註解進行檢測:
public interface BookAuditCrudRepository extends CrudRepository<BookAudit, String> {
}public interface BookDocumentCrudRepository extends CrudRepository<BookDocument, String> {
}
現在,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
}
我們選擇 bookId 和 rentalRecNo 組合作為我們的唯一標識標準,因為我們的應用程序每次當有人借閲書籍時,都會簡單地記錄一條新的借閲記錄。
對於 BookDocument.java,我們使用 @Document 註解,該註解是 MongoDB 專用的:
@Document
public class BookDocument {
private String bookId;
private String bookName;
private String bookAuthor;
private String content;
// standard getters and setters
}
使用 <em>CrudRepository</em> 觸發 <em>BookDocument</em> 的保存仍然成功,但返回類型已從 <em>List</em> 變為 <em>Iterable</em>:
@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. 使用基於包的命名空間
最後,我們可以指定我們的倉庫定義的基礎包,通過使用 <em @EnableCassandraRepositories</em> 和 <em @EnableMongoRepositories</em> 註解來實現:
@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);
}
}
如我們所見,在第 1 行和第 2 行,此配置模式假設我們使用 Cassandra 和 MongoDB 倉庫的不同軟件包
5. 結論
在本教程中,我們配置了一個簡單的 Spring Boot 應用程序,使其同時使用兩個不同的 Spring Data 模塊,並有三種方式實現。
首先,我們擴展了 <em >CassandraRepository</em> 和 <em >MongoRepository</em>,並使用簡單的類來定義領域對象。
其次,我們擴展了通用的 <em >CrudRepository</em> 接口,並依賴於模塊特定的註解,如 <em >@Table</em> 和 <em >@Document</em>,應用於我們的管理對象。
最後,我們使用基於包的檢測,通過 <em >@EnableCassandraRepositories</em> 和 <em >@EnableMongoRepositories</em> 配置應用程序。