1. Введение
Иногда нам нужно подключиться к нескольким технологиям баз данных в одном приложении.
В этом руководстве мы рассмотрим различные варианты конфигурации, когда речь идет об использовании нескольких модулей Spring Data в одном приложении .
Давайте воспользуемся игрушечным книжным магазином Spring Boot, чтобы изучить эту тему.
2. Требуемые зависимости
Во-первых, нам нужно добавить наши зависимости в файл 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. Настройка базы данных
Затем нам нужно настроить фактические базы данных , используя предварительно созданные образы 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
) внутри контейнеров в реальной среде ОС, чтобы наше приложение могло получить доступ к базам данных.
Мы должны создать структуру базы данных для Cassandra с помощью утилиты cqlsh
. Создание пространства ключей не может быть выполнено CassandraDataAutoConfiguration
автоматически , поэтому нам нужно объявить его с использованием синтаксиса CQL .
Итак, сначала мы присоединяемся к оболочке bash
контейнера Cassandra:
$ 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 foreach
WITH replication = {'class':'SimpleStrategy', 'replication_factor':1};
cqlsh> USE foreach;
cqlsh> CREATE TABLE bookaudit(
bookid VARCHAR,
rentalrecno VARCHAR,
loandate VARCHAR,
loaner VARCHAR,
primary key(bookid, rentalrecno)
);
Затем в строках 6 и 9 мы создаем соответствующее пространство ключей и таблицу .
Мы могли бы пропустить создание таблицы и просто положиться на spring-boot-starter-data-cassandra
для инициализации схемы за нас, однако, поскольку мы хотим изучить конфигурации фреймворка по отдельности, это необходимый шаг.
По умолчанию Mongo не требует какой-либо проверки схемы, поэтому для него не нужно ничего дополнительно настраивать . ``
Наконец, мы настраиваем соответствующую информацию о базах данных в нашем application.properties
:
spring.data.cassandra.username=cassandra
spring.data.cassandra.password=cassandra
spring.data.cassandra.keyspaceName=foreach
spring.data.cassandra.contactPoints=localhost
spring.data.cassandra.port=9042
spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017
spring.data.mongodb.database=foreach
4. Механизмы обнаружения хранилища данных
Когда в пути к классам обнаруживается несколько модулей 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", "ForEach", "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 теперь становится аннотированным с помощью @Table, специфичной для Cassandra
,
и требует составного первичного ключа:
@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
}
Запуск сохранения BookDocument
с помощью CrudRepository
по-прежнему выполняется успешно, но возвращаемый тип в строке 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. Использование области видимости на основе пакетов
Наконец, мы можем указать базовые пакеты, в которых определены наши репозитории , используя аннотации @EnableCassandraRepositories
и @EnableMongoRepositories
:
@EnableCassandraRepositories(basePackages="com.foreach.multipledatamodules.cassandra")
@EnableMongoRepositories(basePackages="com.foreach.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 тремя способами.
В качестве первого примера мы расширили CassandraRepository
и MongoRepository
и использовали простые классы для объектов предметной области.
В нашем втором подходе мы расширили общий интерфейс CrudRepository
и полагались на аннотации для конкретных модулей, такие как @Table
и @Document
, для наших управляемых объектов.
В конце концов, мы использовали обнаружение на основе пакетов, когда настраивали приложение с помощью @EnableCassandraRepositories
и @EnableMongoRepositories
.
Как всегда, полный код доступен на GitHub .