RethinkDB 入門
一、簡介
在本文中,我們將了解RethinkDB 。這是一個開源的 NoSQL 數據庫,專為實時應用程序而設計。我們將看到它為我們的應用程序帶來了哪些特性,我們可以用它做什麼,以及如何與它交互。
2. 什麼是 RethinkDB?
RethinkDB 是一個強調可伸縮性和高可用性的開源 NoSQL 數據庫。它允許我們存儲稍後可以查詢的 JSON 文檔。我們還能夠在我們的數據庫中執行跨多個表的連接,並對我們的數據執行 map-reduce 函數。
然而,讓 RethinkDB 脫穎而出的是它的實時流功能。我們可以對我們的數據庫執行查詢,以便對結果集的更改不斷地流回客戶端,從而使我們能夠實時更新我們的數據。這意味著我們的應用程序可以在任何變化時立即向我們的用戶提供更新。
3. 運行和使用 RethinkDB
RethinkDB 是一個用 C++ 編寫的本地應用程序。大多數平台都可以使用預構建的包。還有一個官方 Docker 鏡像。
安裝後,我們可以通過簡單地運行可執行文件來啟動數據庫。如有必要,我們可以告訴它存儲數據文件的位置,但如果不需要,這將有一個合理的默認值。我們還可以配置它偵聽的端口,甚至可以在集群配置中運行多個服務器以實現擴展和可用性。所有這些以及更多內容都可以在官方文檔中看到。
然後我們需要實際使用我們應用程序中的數據庫。這將要求我們使用適當的客戶端連接到它——具有多種語言的選項。但是,對於本文,我們將使用Java 客戶端。
將客戶端添加到我們的應用程序就像添加一個依賴項一樣簡單:
<dependency>
<groupId>com.rethinkdb</groupId>
<artifactId>rethinkdb-driver</artifactId>
<version>2.4.4</version>
</dependency>
接下來,我們實際上需要連接到數據庫。為此,我們需要一個Connection
:
Connection conn = RethinkDB.r.connection()
.hostname("localhost")
.port(28015)
.connect();
4. 與 RethinkDB 交互
現在我們已經連接到 RethinkDB,我們需要知道如何使用它。在數據庫的基礎層面,這意味著我們需要能夠創建、操作和檢索數據。
與 RethinkDB 的所有交互都是通過編程接口完成的。我們不是使用自定義查詢語言編寫查詢,而是使用更豐富的類型模型在標準 Java 中編寫它們。這給了我們讓編譯器確保我們的查詢有效而不是在運行時發現我們有問題的優勢。
4.1.使用表格
RethinkDB 實例公開了多個數據庫,每個數據庫都將數據存儲在表中。這些在概念上類似於 SQL 數據庫中的表。但是,RethinkDB 不會在我們的表上強制執行模式,而是將其留給應用程序。
我們可以使用我們的連接創建一個新表:
r.db("test").tableCreate(tableName).run(conn);
同樣,我們可以使用Db.tableDrop().
我們還可以使用Db.tableList()
列出所有已知的表:
r.db(dbName).tableCreate(tableName).run(conn);
List<String> tables = r.db(dbName).tableList().run(conn, List.class).first();
assertTrue(tables.contains(tableName));
4.2.插入數據
一旦我們有了要使用的表,我們就需要能夠填充它們。我們可以通過使用Table.insert()
並為它提供數據來做到這一點。
讓我們通過提供由 RethinkDB API 本身構造的對象來將一些數據插入到我們的表中:
r.db(DB_NAME).table(tableName)
.insert(r.hashMap().with("name", "Baeldung"))
.run(conn);
或者,我們可以提供標準的 Java 集合:
r.db(DB_NAME).table(tableName)
.insert(Map.of("name", "Baeldung"))
.run(conn);
我們插入的數據可以像單個鍵/值對一樣簡單,也可以像需要的那樣複雜。這可以包括嵌套結構、數組或任何需要的東西:
r.db(DB_NAME).table(tableName)
.insert(
r.hashMap()
.with("name", "Baeldung")
.with("articles", r.array(
r.hashMap()
.with("id", "article1")
.with("name", "String Interpolation in Java")
.with("url", "https://www.baeldung.com/java-string-interpolation"),
r.hashMap()
.with("id", "article2")
.with("name", "Access HTTPS REST Service Using Spring RestTemplate")
.with("url", "https://www.baeldung.com/spring-resttemplate-secure-https-service"))
)
).run(conn);
插入的每條記錄都有一個唯一的 ID——可以是我們在記錄中作為“id”字段提供的 ID,也可以是從數據庫中隨機生成的 ID。
4.3.檢索數據
現在我們有了一個包含一些數據的數據庫,我們需要能夠再次將其取出。與所有數據庫一樣,我們通過查詢數據庫來完成此操作。
我們可以做的最簡單的事情就是查詢一個表而不需要任何額外的東西:
Result<Map> results = r.db(DB_NAME).table(tableName).run(conn, Map.class);
我們的結果對象為我們提供了多種訪問結果的方法,包括能夠將其直接視為迭代器:
for (Map result : results) {
// Process result
}
我們還可以將結果轉換為List
或Stream
包括並行流——如果我們隨後想將結果視為普通的 Java 集合。
如果我們只想檢索結果的一個子集,我們可以在運行查詢時應用過濾器。這是通過提供 Java lambda 來執行查詢來完成的:
Result<Map> results = r.db(DB_NAME)
.table(tableName)
.filter(r -> rg("name").eq("String Interpolation in Java"))
.run(conn, Map.class);
我們的過濾器根據表中的行進行評估,只有匹配的那些才會返回到我們的結果集中。
如果我們知道 ID 值,我們也可以直接轉到單行:
Result<Map> results = r.db(DB_NAME).table(tableName).get(id).run(conn, Map.class);
4.4.更新和刪除數據
一個一旦插入就不能改變數據的數據庫只有有限的用途,那麼我們如何更新我們的數據呢? RethinkDB API 為我們提供了一個update()
方法,我們可以將其鏈接到查詢語句的末尾,以便將這些更新應用於與查詢匹配的每條記錄。
這些更新是補丁,而不是完全替換,所以我們只指定我們想要進行的更改:
r.db(DB_NAME).table(tableName).update(r.hashMap().with("site", "Baeldung")).run(conn);
與查詢一樣,我們可以使用過濾器準確選擇要更新的記錄。這些需要在指定更新之前完成。這是因為過濾器實際上應用於選擇要更新的記錄的查詢,然後更新應用於匹配的所有內容:
r.db(DB_NAME).table(tableName)
.filter(r -> rg("name").eq("String Interpolation in Java"))
.update(r.hashMap().with("category", "java"))
.run(conn);
我們還可以通過在查詢結束時使用 delete() 調用而不是update()
delete()
類似的方式刪除記錄:
r.db(DB_NAME).table(tableName)
.filter(r -> rg("name").eq("String Interpolation in Java"))
.delete()
.run(conn);
5.實時更新
到目前為止,我們已經看到了一些如何與 RethinkDB 交互的示例,但是這些都沒有什麼特別之處。我們所看到的一切也可以通過大多數其他數據庫系統實現。
RethinkDB 的特別之處在於能夠在我們的應用程序無需輪詢數據的情況下實時更新我們的數據。相反,我們可以以游標保持打開的方式執行查詢,並且數據庫會在發生任何更改時將其推送給我們:
Result<Map> cursor = r.db(DB_NAME).table(tableName).changes().run(conn, Map.class);
cursor.stream().forEach(record -> System.out.println("Record: " + record));
這在編寫我們想要立即更新的實時應用程序時非常強大——例如,顯示實時股票價格、遊戲比分或許多其他內容。
當我們執行這樣的查詢時,我們會像以前一樣得到一個游標。但是,添加changes()
意味著我們不會查詢已經存在的記錄。相反,游標將為我們提供與查詢匹配的記錄發生的變化的無限集合。這包括插入、更新和刪除。
游標是無界的這一事實意味著我們對其執行的任何迭代,無論是使用普通的for
循環還是流,都將根據需要持續進行。我們不能安全地做的是收集到一個列表,因為列表沒有盡頭。
我們在游標中返回的記錄包括更改記錄的新值和舊值。然後我們可以確定更改是否是插入(因為沒有舊值)、刪除(因為沒有新值)或更新(因為同時存在舊值和新值)。我們還可以在更新中看到舊值和新值之間的差異並做出相應的反應。
與所有查詢一樣,我們也可以在獲取記錄更改時應用過濾器。這將導致我們的游標僅包含與此過濾器匹配的記錄。這甚至適用於插入,其中記錄在執行時不存在:
Result<Map> cursor = r.db(DB_NAME).table(tableName)
.filter(r -> rg("index").eq(5))
.changes()
.run(conn, Map.class);
六,結論
我們在這裡看到了對 RethinkDB 數據庫引擎的非常簡短的介紹,展示了我們如何將它用於我們所有的傳統數據庫任務,以及如何利用其自動將更改推送到我們的客戶端的獨特功能。這只是一個快速瀏覽,這個系統還有很多,為什麼不自己試試呢?
本文中的所有代碼示例都可以在 GitHub 上找到。