MyBatis動態SQL簡介
1. 引言
MyBatis Dynamic SQL是一個用來以型別安全的方式產生 SQL 語句的函式庫。它幫助我們確保 SQL 語法和參數綁定的有效性。此外,產生的 SQL 是基於代表資料庫的類別定義,因此我們可以利用編譯器來進一步確保 SQL 的正確性。
在本教程中,我們將了解 MyBatis 動態 SQL。我們將了解它是什麼,可以用它做什麼,以及如何使用它。
2. 依賴關係
在使用 MyBatis Dynamic SQL 之前,我們需要將最新版本包含到我們的建置中,截至撰寫本文時,最新版本為1.5.2 。
如果我們使用 Maven,我們可以將此依賴項新增到 pom.xml 檔案中:
<dependency>
<groupId>org.mybatis.dynamic-sql</groupId>
<artifactId>mybatis-dynamic-sql</artifactId>
<version>1.5.2</version>
</dependency>
現在,我們已經準備好在我們的應用程式中使用它了。
3. 資料庫對象
MyBatis Dynamic SQL 透過使用代表資料庫結構的類型來確保 SQL 產生的程式碼類型安全。這包括我們在應用程式中編寫的用於表示我們正在操作的資料庫表和列的類別。
所有這些資料庫物件都是繼承自SqlTable類別的簡單類別:
public class User extends SqlTable {
public User() {
super("users");
}
}
構造函數提供資料庫中表的名稱。在本例中,我們表示資料庫中名為users表。
然後我們可以向表中新增列定義。這些列定義是作為類別中的欄位建立的,由column輔助函數產生:
public class User extends AliasableSqlTable<User> {
public final SqlColumn<Integer> userId = column("user_id");
public final SqlColumn<String> userName = column("username");
}
將這些字段設為公共字段,我們就可以從類別外部引用它們,這是我們產生 SQL 所必需的。
4. 產生 SQL
建構好資料庫物件後,我們需要能夠產生 SQL。 MyBatis **Dynamic SQL 使用SqlBuilder類別中的一個靜態方法(例如SqlBuilder.countFrom().**此方法確定我們要建立的查詢類型,並傳回適合我們使用的類型:
CountDSL<SelectModel> count = SqlBuilder.countFrom(user);
這裡我們將資料庫物件的一個實例傳遞給countFrom()方法。這指明了我們將要操作的表。
我們還可以進一步使用適當的方法來連結此結果,稍後我們將看到。最終,我們呼叫build()方法,該方法傳回一個用於 SQL 語句的模型類別:
SelectModel model = SqlBuilder.countFrom(user)
.build();
接下來,我們可以呼叫render()方法來實際渲染 SQL 語句:
SelectStatementProvider sqlStatement = SqlBuilder.select(user.allColumns())
.from(user)
.build()
.render(RenderingStrategies.SPRING_NAMED_PARAMETER);
此方法需要使用渲染策略。我們有兩種標準選項可供選擇——一種用於 MyBatis3,另一種用於 Spring。
render()呼叫傳回的語句提供者可讓我們存取實際的 SQL 語句以及呼叫該 SQL 語句所需的綁定參數:
String sql = sqlStatement.getSelectStatement();
Map<String, Object> binds = sqlStatement.getParameters();
然後我們可以將這些資訊提供給資料庫調用,以實際執行查詢。
5. 選擇語句
現在我們已經知道如何產生 SQL 並將參數與資料庫物件綁定,我們就可以開始編寫一些語句了。
選擇語句是我們能夠建立的最複雜的語句之一,因為它們包含各種部分,並且可以以各種方式運行。
所有 SELECT 語句都以以下幾個靜態方法之一開頭:
-
select()– 開始建立一個普通的SELECT語句,以傳回一組欄位。 -
selectDistinct()– 開始建立SELECT DISTINCT語句以傳回一組欄位。 -
countFrom()– 開始建立SELECTCOUNT(*) 語句,以從表中傳回計數。 -
countColumn()– 開始建構SELECT COUNT(column)語句,以計算指定欄位中的非空值。 -
countDistinctColumn()– 開始建立SELECT COUNT(DISTINCT column)語句,以計算指定列中不同的非空值。
除了countFrom()函數之外,其他每個函數都需要提供要從中選擇資料的表:
User user = new User();
SelectStatementProvider sql = SqlBuilder.select(user.allColumns())
.from(user)
.build()
.render(RenderingStrategies.SPRING_NAMED_PARAMETER);
// SELECT * FROM users
我們還需要指定要傳回哪些列。這可以是先前建立的任何欄位定義,也可以是特殊值allColumns().
我們也可以為選定的列使用一些特殊的聚合值。 SqlBuilder SqlBuilder提供了count() 、 max() 、 sum()等方法。我們可以像使用其他欄位定義一樣使用這些方法,結果也完全符合預期。
5.1. where 條款
預設情況下,我們的 SELECT 語句會傳回所有結果。不過,我們可以加入WHERE子句來篩選結果。這些子句在指定表格之後就開始使用where()方法:
SelectStatementProvider sql = SqlBuilder.select(user.allColumns())
.from(user)
.where(user.userName, SqlBuilder.isEqualTo("baeldung"))
.build()
.render(RenderingStrategies.SPRING_NAMED_PARAMETER);
這裡,` where()方法接受要比較的列和要使用的比較條件。 `SquBuilder` SquBuilder提供了所有我們需要進行的比較的靜態方法。此外,還有一些替代版本可用於更複雜的條件,例如帶有子查詢的EXISTS子句。
MyBatis Dynamic SQL 在這裡使用了泛型來確保比較的類型安全。用於比較的類型必須與被比較列的類型相符。這裡, userName欄位是SqlColumn<String>類型,因此isEqualTo()方法也必須接受一個字串參數。
產生此 SQL 時,我們現在會得到一個綁定值。上述產生的 SQL 語句如下:
SELECT * FROM users WHERE username = :p1
SelectStatementProvider. getParameters()方法傳回一個包含條目p1的映射,其值為我們的值。
我們也可以用and()和or()來建構更複雜的WHERE子句:
SelectStatementProvider sql = SqlBuilder.select(user.allColumns())
.from(user)
.where(user.userName, SqlBuilder.isEqualTo("baeldung"))
.or(user.userId, SqlBuilder.isGreaterThan(5))
.build()
.render(RenderingStrategies.SPRING_NAMED_PARAMETER);
// SELECT * FROM users WHERE username = :p1 OR user_id > :p2
這樣,我們就可以建構出既滿足型別安全要求又能確保平衡的複雜語句。
5.2. 加入
到目前為止,我們已經了解如何編寫引用單一表的 SQL 語句,但我們經常需要連接多個表。 MyBatis Dynamic SQL 允許我們在建立 SQL 時使用join()方法來實現這一點:
SelectStatementProvider sql = SqlBuilder.select(post.allColumns())
.from(post)
.join(user).on(user.userId, SqlBuilder.equalTo(post.posterId))
.build()
.render(RenderingStrategies.SPRING_NAMED_PARAMETER);
// SELECT posts.* FROM posts JOIN users ON users.user_id = posts.poster_id
這裡,` join()方法接收要連接的資料庫表的物件。然後我們需要使用 ` on()方法來指定連線的具體方式。請注意,這裡使用的是equalTo而不是isEqualTo 。這是因為條件類型不同,因此需要使用不同的 Java 類型以確保類型安全。
我們還可以使用幾種不同的連接方法,這取決於我們要實現的目標:
-
join()– 這將會建立一個標準的JOIN … ON …語句。 -
leftJoin()– 這將會建立一個LEFT JOIN … ON …語句。 -
rightJoin()– 這將會建立一個LEFT JOIN … ON …語句。 -
fullJoin()– 這將會建立一個FULL JOIN … ON …語句。
這些操作方式都相同,唯一的差別在於產生的連線類型。
6. 其他聲明
除了SELECT語句之外,我們還可以建立其他 SQL 語句。
DELETE語句與我們先前看到的語句最為相似。我們先使用SqlBuilder.deleteFrom()方法,然後提供適當的WHERE子句:
DeleteStatementProvider sql = SqlBuilder.deleteFrom(user)
.where(user.userId, isEqualTo(1))
.build()
.render(RenderingStrategies.SPRING_NAMED_PARAMETER);
// DELETE FROM users WHERE user_id = :p1
UPDATE語句與之類似,但它也支援在語句中設定欄位值:
UpdateStatementProvider sql = SqlBuilder.update(user)
.set(user.userName).equalTo("Baeldung")
.where(user.userId, isEqualTo(1))
.build()
.render(RenderingStrategies.SPRING_NAMED_PARAMETER);
// UPDATE users SET username = :p1 WHERE user_id = :p2
最後, INSERT語句要求我們提供新值,但不支援WHERE子句:
GeneralInsertStatementProvider sql = SqlBuilder.insertInto(user)
.set(user.userId).toValue(2)
.set(user.userName).toValue("Baeldung")
.build()
.render(RenderingStrategies.SPRING_NAMED_PARAMETER);
// INSERT INTO users(user_id, username) VALUES (:p1, :p2)
所有這些最終都會傳回一個適當的語句提供者實例,然後我們可以使用該實例來取得 SQL 和綁定參數,以便針對資料庫執行我們的語句。
8. 總結
以上是對 MyBatis Dynamic SQL 的簡要介紹。這個函式庫的功能遠不止於此。下次需要安全地產生 SQL 語句時,不妨試試它。
與往常一樣,本文中的所有範例都可以在 GitHub 上找到。