[譯] RxJava JDBC 簡介

Oopsguy發表於2019-03-03

www.baeldung.com/rxjava-jdbc
作者:baeldung
譯者:oopsguy.com

1、概述

簡單地說,rxjava-jdbc 是一個用於與關聯式資料庫互動的 API,其允許以鏈式的方式呼叫。在此快速教程中,我們將來了解這個類庫,以及如何使用它的一些常用功能。

在閱讀本教程之前,你需要有一定的 RxJava 基礎知識。

2、Maven 依賴

從 Maven 依賴開始,我們需要把依賴新增到 pom.xml 檔案中:

<dependency>
    <groupId>com.github.davidmoten</groupId>
    <artifactId>rxjava-jdbc</artifactId>
    <version>0.7.11</version>
</dependency>複製程式碼

我們可以在 Maven Central 上找到最新版本的 API。

3、主要元件

Database 類是執行所有常見型別資料庫互動的主入口點。要建立一個 Database 物件,我們可以將 ConnectionProvider 介面實現例項傳遞給 from() 靜態方法:

public static ConnectionProvider connectionProvider
  = new ConnectionProviderFromUrl(
  DB_CONNECTION, DB_USER, DB_PASSWORD);
Database db = Database.from(connectionProvider);複製程式碼

ConnectionProvider 有幾個值得注意的實現 — 例如 ConnectionProviderFromContextConnectionProviderFromDataSourceConnectionProviderFromUrlConnectionProviderPooled

為了做些基本操作,我們可以使用以下 Database 的 API:

  • select() — 用於 SQL select 查詢
  • update() — 用於 DDL 語句,如 create 和 drop,以及 insert、update 和 delete

4、啟動

在下面的快速示例中,我們將展示基本的資料庫操作:

public class BasicQueryTypesTest {

    Observable<Integer> 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<String> 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、自動對映

自動對映功能允許我們將指定的資料庫記錄對映到物件。

我們來看看兩種資料庫記錄的自動對映方法。

5.1、使用介面自動對映

我們可以使用帶註解的介面將資料庫記錄 automap() 到物件。為此,我們可以建立一個帶註解的介面:

public interface Employee {

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

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

}複製程式碼

之後,執行測試:

@Test
public void whenSelectFromTableAndAutomap_thenCorrect() {
    List<Employee> 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<Manager> 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");
}複製程式碼

這裡有幾個要點:

  • createinsert1insert2 引用了在建立 Manager 表並將記錄插入其中返回的 Observables
  • 我們查詢中所 select 的列的數量必須與 Manager 類構造方法中的引數數量相匹配
  • 列必須是可以自動對映到構造方法中的型別

有關自動對映的更多資訊,請訪問 GitHub 上的 rxjava-jdbc 倉庫

6、使用大物件

API 支援使用大物件(如 CLOB 和 BLOBS)。在接下來的小節中,我們將介紹如何利用這一功能。

6.1、CLOB

我們來如何 insert 和 select 一個 CLOB:

@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 的內容轉換為 String

6.2、BLOB

我們可以使用 API 以類似的方式使用 BLOB。唯一的區別是,我們不必傳遞一個 StringtoSentinelIfNull() 方法,而是傳遞一個位元組陣列。

我們可以這樣做:

@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<Boolean> begin = db.beginTransaction();
    Observable<Integer> createStatement = db.update(
      "CREATE TABLE IF NOT EXISTS EMPLOYEE(id int primary key, name varchar(255))")
      .dependsOn(begin)
      .count();
    Observable<Integer> insertStatement = db.update(
      "INSERT INTO EMPLOYEE(id, name) VALUES(1, `John`)")
      .dependsOn(createStatement)
      .count();
    Observable<Integer> updateStatement = db.update(
      "UPDATE EMPLOYEE SET name = `Tom` WHERE id = 1")
      .dependsOn(insertStatement)
      .count();
    Observable<Boolean> 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() 方法捕獲 Exception 來回滾整個事務,以防程式碼由於某些原因而失敗。我們可以為所有 Exception 或指定 Exception 進行此操作。

8、返回生成的 key

如果我們在處理的表中設定了 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 上獲取完整的程式碼。

相關連結和原文程式碼

相關文章