«1. Введение

Иногда нам нужно подключиться к нескольким технологиям баз данных в одном приложении.

В этом руководстве мы рассмотрим различные параметры конфигурации, когда речь идет об использовании нескольких модулей Spring Data в одном приложении.

Давайте воспользуемся игрушечным книжным магазином Spring Boot, чтобы изучить тему.

2. Требуемые зависимости

Во-первых, нам нужно добавить наши зависимости в файл pom.xml, чтобы мы могли использовать spring-boot-starter-data-mongodb и spring-boot-starter-data-cassandra Привязки данных Spring Boot:

<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
[email protected]:/# 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 мы затем создаем соответствующее пространство ключей и таблицу.


Мы могли бы пропустить создание таблицы и просто положиться на spring-boot-starter-data-cassandra для инициализации схемы за нас, однако, поскольку мы хотим изучить конфигурации фреймворка по отдельности, это необходимый шаг.

По умолчанию Mongo не требует никакой проверки схемы, поэтому для него не нужно ничего настраивать.

Наконец, мы настраиваем соответствующую информацию о базах данных в наших 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. Механизмы обнаружения хранилища данных

Когда в пути к классам обнаруживается несколько модулей 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> {

}

@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
}

BookAudit.java теперь становится аннотированным с помощью @Table, характерной для Cassandra, и требует составного первичного ключа: ~ ~~

Мы выбираем комбинацию bookId и RentalRecNo в качестве наших уникальных критериев, поскольку наше приложение просто регистрирует новую запись об аренде каждый раз, когда кто-то берет книгу напрокат.

@Document
public class BookDocument {

  private String bookId;
  private String bookName;
  private String bookAuthor;
  private String content;
 
  // standard getters and setters
}

Для BookDocument.java мы используем аннотацию @Document, специфичную для MongoDB:

@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));
}

Запуск сохранения BookDocument с помощью CrudRepository по-прежнему успешен, но возвращаемый тип в строке 11 теперь является Iterable, а не Список:

4.3. Использование области видимости на основе пакетов

@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);
  }
}

Наконец, мы можем указать базовые пакеты, в которых определены наши репозитории, используя аннотации @EnableCassandraRepositories и @EnableMongoRepositories:

«

«Как мы видим в строках 1 и 2, этот режим конфигурации предполагает, что мы используем разные пакеты для репозиториев Cassandra и MongoDB.

5. Заключение

В этом руководстве мы настроили простое приложение Spring Boot для использования двух разных модулей Spring Data тремя способами.

В качестве первого примера мы расширили CassandraRepository и MongoRepository и использовали простые классы для объектов предметной области.

В нашем втором подходе мы расширили общий интерфейс CrudRepository и полагались на аннотации для конкретных модулей, такие как @Table и @Document, в наших управляемых объектах.