Рубрики
Без рубрики

Введение в rxjava-jdbc

Узнайте, как использовать возможности RxJava в контексте JDBC.

Автор оригинала: baeldung.

1. Обзор

Проще говоря, rxjava-jdbc-это API для взаимодействия с реляционными базами данных, который позволяет свободно вызывать методы. В этом кратком руководстве мы рассмотрим библиотеку и то, как мы можем использовать некоторые из ее общих функций.

Если вы хотите узнать основы RxJava, ознакомьтесь с этой статьей .

Дальнейшее чтение:

Введение в RxJava

Работа с противодавлением с помощью RxJava

Наблюдаемые операторы коммунальных услуг в RxJava

2. Зависимость Maven

Давайте начнем с зависимости Maven, которую нам нужно добавить в ваш pom.xml :


    com.github.davidmoten
    rxjava-jdbc
    0.7.11

Мы можем найти последнюю версию API на Maven Central .

3. Основные Компоненты

Класс Database является основной точкой входа для запуска всех распространенных типов взаимодействий с базами данных. Чтобы создать объект Database , мы можем передать экземпляр реализации интерфейса Connection Provider в статический метод from() :

public static ConnectionProvider connectionProvider
  = new ConnectionProviderFromUrl(
  DB_CONNECTION, DB_USER, DB_PASSWORD);
Database db = Database.from(connectionProvider);

ConnectionProvider имеет несколько реализаций, на которые стоит обратить внимание, таких как ConnectionProviderFromContext , ConnectionProviderFromDataSource , ConnectionProviderFromUrl и ConnectionProviderPooled .

Для выполнения основных операций мы можем использовать следующие API-интерфейсы Database :

  • select() – используется для запросов SQL select
  • update() – используется для операторов DDL, таких как create и drop, а также insert, update и delete

4. Запуск

В следующем кратком примере мы покажем, как мы можем выполнять все основные операции с базой данных:

public class BasicQueryTypesTest {
    
    Observable create,
      insert1, 
      insert2, 
      insert3, 
      update, 
      delete = null;
    
    @Test
    public void whenCreateTableAndInsertRecords_thenCorrect() {
        create = db.update(
          "CREATE TABLE IF NOT EXISTS EMPLOYEE("
          + "id int primary key, name varchar(255))")
          .count();
        insert1 = db.update(
          "INSERT INTO EMPLOYEE(id, name) VALUES(1, 'John')")
          .dependsOn(create)
          .count();
        update = db.update(
          "UPDATE EMPLOYEE SET name = 'Alan' WHERE id = 1")
          .dependsOn(create)
          .count();
        insert2 = db.update(
          "INSERT INTO EMPLOYEE(id, name) VALUES(2, 'Sarah')")
          .dependsOn(create)
          .count();
        insert3 = db.update(
          "INSERT INTO EMPLOYEE(id, name) VALUES(3, 'Mike')")
          .dependsOn(create)
          .count();
        delete = db.update(
          "DELETE FROM EMPLOYEE WHERE id = 2")
          .dependsOn(create)
          .count();
        List names = db.select(
          "select name from EMPLOYEE where id < ?")
          .parameter(3)
          .dependsOn(create)
          .dependsOn(insert1)
          .dependsOn(insert2)
          .dependsOn(insert3)
          .dependsOn(update)
          .dependsOn(delete)
          .getAs(String.class)
          .toList()
          .toBlocking()
          .single();
        
        assertEquals(Arrays.asList("Alan"), names);
    }
}

Краткое примечание здесь – мы вызываем dependsOn () , чтобы определить порядок выполнения запросов.

В противном случае код завершится ошибкой или приведет к непредсказуемым результатам, если мы не определим, в какой последовательности мы хотим, чтобы запросы выполнялись.

5. Automap

Функция automap позволяет нам сопоставлять выбранные записи базы данных с объектами.

Давайте рассмотрим два способа автоматического сопоставления записей базы данных.

5.1. Автоматизация с использованием интерфейса

Мы можем automap() записи базы данных в объекты с использованием аннотированных интерфейсов. Для этого мы можем создать аннотированный интерфейс:

public interface Employee {

    @Column("id")
    int id();

    @Column("name")
    String name();
}

Затем мы можем запустить наш тест:

@Test
public void whenSelectFromTableAndAutomap_thenCorrect() {
    List employees = db.select("select id, name from EMPLOYEE")
      .dependsOn(create)
      .dependsOn(insert1)
      .dependsOn(insert2)
      .autoMap(Employee.class)
      .toList()
      .toBlocking()
      .single();
    
    assertThat(
      employees.get(0).id()).isEqualTo(1);
    assertThat(
      employees.get(0).name()).isEqualTo("Alan");
    assertThat(
      employees.get(1).id()).isEqualTo(2);
    assertThat(
      employees.get(1).name()).isEqualTo("Sarah");
}

5.2. Автоматическое сопоставление с использованием класса

Мы также можем автоматически сопоставлять записи базы данных с объектами, используя конкретные классы. Давайте посмотрим, как может выглядеть класс:

public class Manager {

    private int id;
    private String name;

    // standard constructors, getters, and setters
}

Теперь мы можем запустить наш тест:

@Test
public void whenSelectManagersAndAutomap_thenCorrect() {
    List managers = db.select("select id, name from MANAGER")
      .dependsOn(create)
      .dependsOn(insert1)
      .dependsOn(insert2)
      .autoMap(Manager.class)
      .toList()
      .toBlocking()
      .single();
    
    assertThat(
      managers.get(0).getId()).isEqualTo(1);
    assertThat(
     managers.get(0).getName()).isEqualTo("Alan");
    assertThat(
      managers.get(1).getId()).isEqualTo(2);
    assertThat(
      managers.get(1).getName()).isEqualTo("Sarah");
}

Несколько заметок здесь:

  • create , insert1 и insert2 – это ссылки на Наблюдаемые , возвращаемые при создании таблицы Manager и вставке в нее записей
  • Количество выбранных столбцов в запросе должно соответствовать количеству параметров в конструкторе класса Manager
  • Столбцы должны быть типов, которые могут быть автоматически сопоставлены с типами в конструкторе

Для получения дополнительной информации об автоматическом сопоставлении посетите репозиторий rxjava-jdbc на GitHub

6. Работа С Большими Объектами

API поддерживает работу с большими объектами, такими как сгустки и большие двоичные объекты. В следующих подразделах мы рассмотрим, как мы можем использовать эту функциональность.

6.1. Клубы

Давайте посмотрим, как мы можем вставить и выбрать КЛУБ:

@Before
public void setup() throws IOException {
    create = db.update(
      "CREATE TABLE IF NOT EXISTS " + 
      "SERVERLOG (id int primary key, document CLOB)")
        .count();
    
    InputStream actualInputStream
      = new FileInputStream("src/test/resources/actual_clob");
    actualDocument = getStringFromInputStream(actualInputStream);

    InputStream expectedInputStream = new FileInputStream(
      "src/test/resources/expected_clob");
 
    expectedDocument = getStringFromInputStream(expectedInputStream);
    insert = db.update(
      "insert into SERVERLOG(id,document) values(?,?)")
        .parameter(1)
        .parameter(Database.toSentinelIfNull(actualDocument))
      .dependsOn(create)
      .count();
}

@Test
public void whenSelectCLOB_thenCorrect() throws IOException {
    db.select("select document from SERVERLOG where id = 1")
      .dependsOn(create)
      .dependsOn(insert)
      .getAs(String.class)
      .toList()
      .toBlocking()
      .single();
    
    assertEquals(expectedDocument, actualDocument);
}

Обратите внимание, что getStringFromInputStream () – это метод, который преобразует содержимое InputStream в строку.

6.2. Капли

Мы можем использовать API для работы с большими двоичными объектами очень похожим образом. Единственное отличие заключается в том, что вместо передачи String в метод to Sentinel If Null() мы должны передать массив байтов.

Вот как мы можем это сделать:

@Before
public void setup() throws IOException {
    create = db.update(
      "CREATE TABLE IF NOT EXISTS " 
      + "SERVERLOG (id int primary key, document BLOB)")
        .count();
    
    InputStream actualInputStream
      = new FileInputStream("src/test/resources/actual_clob");
    actualDocument = getStringFromInputStream(actualInputStream);
    byte[] bytes = this.actualDocument.getBytes(StandardCharsets.UTF_8);
    
    InputStream expectedInputStream = new FileInputStream(
      "src/test/resources/expected_clob");
    expectedDocument = getStringFromInputStream(expectedInputStream);
    insert = db.update(
      "insert into SERVERLOG(id,document) values(?,?)")
      .parameter(1)
      .parameter(Database.toSentinelIfNull(bytes))
      .dependsOn(create)
      .count();
}

Затем мы можем повторно использовать тот же тест в предыдущем примере.

7. Сделки

Далее давайте рассмотрим поддержку транзакций.

Управление транзакциями позволяет нам обрабатывать транзакции, которые используются для группировки нескольких операций с базой данных в одну транзакцию, чтобы все они могли быть зафиксированы – навсегда сохранены в базе данных или полностью откатаны.

Давайте рассмотрим краткий пример:

@Test
public void whenCommitTransaction_thenRecordUpdated() {
    Observable begin = db.beginTransaction();
    Observable createStatement = db.update(
      "CREATE TABLE IF NOT EXISTS EMPLOYEE(id int primary key, name varchar(255))")
      .dependsOn(begin)
      .count();
    Observable insertStatement = db.update(
      "INSERT INTO EMPLOYEE(id, name) VALUES(1, 'John')")
      .dependsOn(createStatement)
      .count();
    Observable updateStatement = db.update(
      "UPDATE EMPLOYEE SET name = 'Tom' WHERE id = 1")
      .dependsOn(insertStatement)
      .count();
    Observable commit = db.commit(updateStatement);
    String name = db.select("select name from EMPLOYEE WHERE id = 1")
      .dependsOn(commit)
      .getAs(String.class)
      .toBlocking()
      .single();
    
    assertEquals("Tom", name);
}

Чтобы начать транзакцию, мы вызываем метод beginTransaction() . После вызова этого метода каждая операция базы данных выполняется в одной транзакции до тех пор, пока не будет вызван любой из методов commit() или rollback () .

Мы можем использовать метод rollback() при перехвате Исключения для отката всей транзакции в случае сбоя кода по какой-либо причине. Мы можем сделать это для всех Исключений или конкретных ожидаемых Исключений .

8. Возврат Сгенерированных Ключей

Если мы установим поле auto_increment в таблице, над которой мы работаем, нам может потребоваться получить сгенерированное значение. Мы можем сделать это, вызвав метод returnGeneratedKeys () .

Давайте рассмотрим краткий пример:

@Test
public void whenInsertAndReturnGeneratedKey_thenCorrect() {
    Integer key = db.update("INSERT INTO EMPLOYEE(name) VALUES('John')")
      .dependsOn(createStatement)
      .returnGeneratedKeys()
      .getAs(Integer.class)
      .count()
      .toBlocking()
      .single();
 
    assertThat(key).isEqualTo(1);
}

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

В этом уроке мы рассмотрели, как использовать методы свободного стиля rxjava jdbc. Мы также рассмотрели некоторые функции, которые он предоставляет, такие как автоматическое сопоставление, работа с большими объектами и транзакциями.

Как всегда, полная версия кода доступна на GitHub .