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 { Observablecreate, 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() { Listemployees = 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() { Listmanagers = 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() { Observablebegin = 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 .