jOOQ(Java 物件導向查詢)是一個功能強大的庫,它使我們能夠以物件導向的方式編寫 SQL 查詢,從而簡化了 Java 中的資料庫互動。連線表是關聯式資料庫中的基本操作,允許我們根據特定條件組合多個表中的資料。在本教程中,我們將探索 jOOQ 中可用的各種型別的聯接。
設定jOOQ
使用 jOOQ 連線兩個表涉及利用 jOOQ 提供的 DSL(域特定語言)來構造 SQL 查詢。
要使用 jOOQ,我們需要將jOOQ和PostgreSQL依賴項新增到 Maven 專案的pom.xml檔案中:
<dependency> <groupId>org.jooq</groupId> <artifactId>jooq</artifactId> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> </dependency>
|
在使用join之前,我們需要使用jOOQ建立與資料庫的連線。我們建立一個方法getConnection()來獲取用於資料庫互動的DSLContext物件:
public static DSLContext getConnection() { try { Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD); DSLContext context = DSL.using(conn, SQLDialect.POSTGRES); return context; } catch (SQLException e) { throw new RuntimeException(e); } }
|
我們將在整個教程中使用上下文物件與資料庫互動:DSLContext context = DBConnection.getConnection();
此外,jOOQ 提供了一個程式碼生成器,可以根據我們的資料庫模式生成 Java 類。我們假設表Store、Book和BookAuthor 是在資料庫中使用各自的架構建立的。
接下來,我們可以使用DSLContext物件在註釋為@BeforeClass 的方法中插入測試資料,以確保它在每次測試之前執行。讓我們將測試資料插入整合到我們的設定方法中:
@BeforeClass public static void setUp() throws Exception { context = DBConnection.getConnection(); context.insertInto(Tables.STORE, Store.STORE.ID, Store.STORE.NAME) .values(1, <font>"ABC Branch I ") .values(2, "ABC Branch II") .execute(); context.insertInto(Tables.BOOK, Book.BOOK.ID, Book.BOOK.TITLE, Book.BOOK.DESCRIPTION, Book.BOOK.AUTHOR_ID, Book.BOOK.STORE_ID) .values(1, "Article 1", "This is article 1", 1, 1) .values(2, "Article 2", "This is article 2", 2, 2) .values(3, "Article 3", "This is article 3", 1, 2) .values(4, "Article 4", "This is article 4", 5, 1) .execute(); context.insertInto(Tables.BOOKAUTHOR, Bookauthor.BOOKAUTHOR.ID, Bookauthor.BOOKAUTHOR.NAME, Bookauthor.BOOKAUTHOR.COUNTRY) .values(1, "John Smith", "Japan") .values(2, "William Walce", "Japan") .values(3, "Marry Sity", "South Korea") .values(4, "Morry Toh", "England") .execute(); }
|
使用join 子句
在 jOOQ 中,SelectJoinStep<Record>是一個介面,表示構建帶有聯接的SELECT查詢的過程中的一個步驟。我們可以使用select()等方法來指定要從相關表中檢索哪些列。
jOOQ中的join()方法用於根據指定條件在表之間執行內連線。內部聯接檢索兩個表中都滿足特定條件的行。
以下是根據作者 ID連線Book和BookAuthor表的示例:
SelectJoinStep<Record> query = context.select() .from(Tables.BOOK) .join(Tables.BOOKAUTHOR) .on(field(Tables.BOOK.AUTHOR_ID).eq(field(Tables.BOOKAUTHOR.ID))); assertEquals(3, query.fetch().size());
|
這是一個演示連線多個表的擴充套件示例:
SelectJoinStep<Record> query = context.select() .from(Tables.BOOK) .join(Tables.BOOKAUTHOR) .on(field(Tables.BOOK.AUTHOR_ID).eq(field(Tables.BOOKAUTHOR.ID))) .join(Tables.STORE) .on(field(Tables.BOOK.STORE_ID).eq(field(Tables.STORE.ID))); assertEquals(3, query.fetch().size());
|
我們向Store表新增了另一個聯接。此連線操作 根據Book表中的STORE_ID列和Store表中的 ID 列連線Book和Store表。透過新增此附加聯接,查詢現在從三個表中檢索資料:Book、BookAuthor和Store。
使用外連線
除了預設的內連線之外,jOOQ還支援各種連線型別,例如外連線。即使連線表中沒有匹配的記錄,外連線也允許我們檢索記錄。
1.左外連線
左聯接包括左表Book中的所有行以及右表BookAuthor中的匹配行。右表中任何不匹配的行對於特定於作者的列都將具有空值。
讓我們看看如何使用 jOOQ 執行左外連線:
SelectJoinStep<Record> query = context.select() .from(Tables.BOOK) .leftOuterJoin(Tables.BOOKAUTHOR) .on(field(Tables.BOOK.AUTHOR_ID).eq(field(Tables.BOOKAUTHOR.ID))); assertEquals(4, query.fetch().size());
|
在輸出中,最後一行的作者列顯示null,而不是相應的作者條目:
+----+---------+---------+-----------------+--------+------+-------------+-------+ | id|author_id|title |description |store_id| id|name |country| +----+---------+---------+-----------------+--------+------+-------------+-------+ | 1| 1| Book 1|This is book 1| 1| 1|John Smith |Japan | | 2| 2| Book 2|This is book 2| 2| 2|William Walce|Japan | | 3| 1| Book 3|This is book 3| 2| 1|John Smith |Japan | | 4| 5| Book 4|This is book 4| 1|{null}|{null} |{null} | +----+---------+---------+-----------------+--------+------+-------------+-------+
|
執行左外連線時,如查詢所示,左表Book中的所有行都 包含在結果集中。在這種情況下,即使BookAuthor表中 最後一行沒有匹配的author_id ,它仍然出現在輸出中。但是,由於BookAuthor表中沒有可用的相應資料 ,因此該行的特定於作者的列(id、name、Country)具有空值。2.右外連線
相反,右連線包含右表BookAuthor中的所有行,並將它們與左表Book中的行進行匹配。左表中與右表中任何條目都不匹配的行對於特定於書籍的列將具有空值。
讓我們看看如何使用 jOOQ 執行右外連線:
SelectJoinStep<Record> query = context.select() .from(Tables.BOOK) .rightOuterJoin(Tables.BOOKAUTHOR) .on(field(Tables.BOOK.AUTHOR_ID).eq(field(Tables.BOOKAUTHOR.ID))); assertEquals(5, query.fetch().size());
|
與左外連線類似,在輸出中,最後兩位作者沒有關聯的圖書記錄,導致空值:
+------+---------+---------+-----------------+--------+----+-------------+-----------+ | id|author_id|title |description |store_id| id|name | country| +------+---------+---------+-----------------+--------+----+-------------+-----------+ ... |{null}| {null}|{null} |{null} | {null}| 4|Morry Toh |England | |{null}| {null}|{null} |{null} | {null}| 3|Marry Sity |South Korea| +------+---------+---------+-----------------+--------+----+-------------+-----------+
|
3.全外連線
完整外連線合並表Book和BookAuthor中的所有行,無論是否存在匹配。在相反的表中沒有匹配項的行的該表中的列具有空值。
要在 jOOQ 中執行完全外連線,我們可以使用以下語法:
SelectJoinStep<Record> query = context.select() .from(Tables.BOOK) .fullOuterJoin(Tables.BOOKAUTHOR) .on(field(Tables.BOOK.AUTHOR_ID).eq(field(Tables.BOOKAUTHOR.ID))); assertEquals(6, query.fetch().size());
|
使用自然連線
自然連線根據匹配的列名自動確定連線條件。當連線條件使用像AUTHOR_ID這樣的公共列很簡單時,這會很有幫助:
SelectJoinStep<Record> query = context.select() .from(Tables.BOOK) .naturalJoin(Tables.BOOKAUTHOR); assertEquals(4, query.fetch().size());
|
但是,如果列名不是用於連線或資料型別不匹配,則可能會出現意外結果。在輸出中,我們觀察到其中一條記錄匹配不正確:
+----+---------+---------+-----------------+--------+----+-------------+-------+ | id|author_id|title |description |store_id| id|name |country| +----+---------+---------+-----------------+--------+----+-------------+-------+ ... | 4| 5| Book 4|This is book 4| 1| 4|Morry Toh |England| +----+---------+---------+-----------------+--------+----+-------------+-------+
|
使用交叉連線
交叉聯接是最基本的聯接型別,其中一個表中的每一行都與另一個表中的每一行組合。這在我們有Store和Book表的特定場景中非常有用。我們想要顯示所有可能的商店-書籍組合的列表。
讓我們檢查一下執行交叉連線時的結果:
SelectJoinStep<Record> query = context.select() .from(Tables.STORE) .crossJoin(Tables.BOOK); assertEquals(8, query.fetch().size());
|
交叉連線有效地產生每種可能的組合,使我們能夠展示“ Branch I – Book 1 ”、“ Branch I – Book 2 ”等選項。但是,由於可能會建立非常大的資料集,尤其是在涉及的表有很多行的情況下,應謹慎使用交叉聯接。結論
在這篇文章中,我們學習瞭如何在 jOOQ 中連線表。我們討論了各種型別的聯接,包括內聯接、外聯接(左聯接、右聯接和全外聯接)、自然聯接和交叉聯接。此外,我們發現自然連線和交叉連線可能很有用,但由於潛在的意外結果或效能問題,應謹慎使用,尤其是對於大型資料集。