Java 永續性/JPQL
Java 永續性查詢語言 (JPQL) 是 JPA 定義的查詢語言。JPQL 類似於 SQL,但它作用於物件、屬性和關係,而不是表和列。JPQL 可用於讀取 (SELECT),以及批次更新 (UPDATE) 和刪除 (DELETE)。JPQL 可用於NamedQuery(透過註解或 XML) 或在使用EntityManager createQuery()API 的動態查詢中。
有關 JPQL BNF,請參閱 BNF。
選擇查詢可用於從資料庫讀取物件。選擇查詢可以返回單個物件或資料元素、物件或資料元素列表,或包含多個物件和資料的物件陣列。
// Query for a List of objects.
Query query = em.createQuery("Select e FROM Employee e WHERE e.salary > 100000");
List<Employee> result = query.getResultList();
// Query for a single object.
Query query = em.createQuery("Select e FROM Employee e WHERE e.id = :id");
query.setParameter("id", id);
Employee result2 = (Employee)query.getSingleResult();
// Query for a single data element.
Query query = em.createQuery("Select MAX(e.salary) FROM Employee e");
BigDecimal result3 = (BigDecimal)query.getSingleResult();
// Query for a List of data elements.
Query query = em.createQuery("Select e.firstName FROM Employee e");
List<String> result4 = query.getResultList();
// Query for a List of element arrays.
Query query = em.createQuery("Select e.firstName, e.lastName FROM Employee e");
List<Object[]> result5 = query.getResultList();
SELECT 子句可以包含物件表示式、屬性表示式、函式、子查詢、建構函式和聚合函式。
聚合函式可以包含關於一組物件的摘要資訊。這些函式包括 MIN、MAX、AVG、SUM、COUNT。這些函式可以用於返回單個結果,或者與 GROUP BY 結合使用以返回多個結果。
SELECT COUNT(e) FROM Employee e
SELECT MAX(e.salary) FROM Employee e
NEW 運算子可以與完全限定的類名一起使用,以從 JPQL 查詢返回資料物件。這些不會是託管物件,並且該類必須定義一個與建構函式的引數及其型別匹配的建構函式。建構函式查詢可用於選擇物件上的部分資料或報告資料,並獲取類例項而不是物件陣列。
SELECT NEW com.acme.reports.EmpReport(e.firstName, e.lastName, e.salary) FROM Employee e
FROM 子句定義要查詢的內容。典型的 FROM 子句將包含要查詢的實體名稱併為其分配別名。
SELECT e FROM Employee e
JPQL 允許查詢多個根級物件。在執行此操作時應謹慎,因為它會導致兩個表的笛卡爾積。WHERE 子句應確保以某種方式連線這兩個物件。
SELECT e, a FROM Employee e, MailingAddress a WHERE e.address = a.address
JPQL 中使用的實體名稱來自 @Entity 註解或 XML 的 name 屬性。它預設為簡單的實體類名。一些 JPA 提供者還允許使用完整類名。
- EclipseLink : TopLink - 允許使用實體的完全限定類名。
JOIN 子句也可以在 FROM 子句中使用。JOIN 子句允許將物件的任何關係連線到查詢中,以便它們可以在 WHERE 子句中使用。JOIN 不意味著關係將被獲取,除非包含 FETCH 選項。
SELECT e FROM Employee e JOIN e.address a WHERE a.city = :city
JOIN 可與 OneToOne、ManyToOne、OneToMany、ManyToMany 和 ElementCollection 對映一起使用。與集合關係一起使用時,您可以多次連線相同的關係以查詢多個獨立值。
SELECT e FROM Employee e JOIN e.projects p JOIN e.projects p2 WHERE p.name = :p1 and p2.name = :p2
FETCH 選項可用於 JOIN 以在單個查詢中獲取相關物件。這避免了對每個物件關係的額外查詢,並確保在關係為 LAZY 時已獲取它們。
SELECT e FROM Employee e JOIN FETCH e.address
JOIN FETCH 不允許在 JPA 規範中使用別名,但一些 JPA 提供者可能允許它。
- EclipseLink : TopLink - 允許使用別名。
預設情況下,JPQL 中的所有連線都是 INNER 連線。這意味著沒有關係的結果將從查詢結果中過濾掉。要避免這種情況,可以使用 LEFT 選項將連線定義為 OUTER 連線。
SELECT e FROM Employee e LEFT JOIN e.address a ORDER BY a.city
用於連線的連線條件來自對映的連線列。這意味著 JPQL 使用者通常可以不必瞭解每個關係是如何連線的。在某些情況下,需要將附加條件附加到連線條件,通常是在外部連線的情況下。這可以透過 ON 子句來完成。ON 子句在 JPA 2.1 規範中定義,一些 JPA 提供者可能支援它。
- EclipseLink : Hibernate : TopLink - 支援 ON 子句。
SELECT e FROM Employee e LEFT JOIN e.address a ON a.city = :city
FROM 子句中的子查詢
[edit | edit source]JPA 不支援 FROM 子句中的子查詢。某些 JPA 提供商可能支援此功能。
- EclipseLink : TopLink - 支援 FROM 子句中的子查詢。
ORDER BY 子句
[edit | edit source]ORDER BY 允許指定結果的排序。可以對多個值進行排序,按升序 (ASC) 或降序 (DESC)。JPA 1.0 和 2.0 BNF 限制了在 ORDER BY 中使用函式和子查詢,但 JPA 2.1 草案刪除了其中大部分限制。
- EclipseLink : TopLink - 允許在 ORDER BY 子句中使用函式、子查詢、NULLS FIRST/LAST、物件表示式和其他操作。
SELECT e FROM Employee e ORDER BY e.lastName ASC, e.firstName, ASC
SELECT e FROM Employee e ORDER BY UPPER(e.lastName)
GROUP BY 子句
[edit | edit source]GROUP BY 允許對一組物件計算彙總資訊。GROUP BY 通常與聚合函式一起使用。
SELECT AVG(e.salary), e.address.city FROM Employee e GROUP BY e.address.city
SELECT AVG(e.salary), e.address.city FROM Employee e GROUP BY e.address.city ORDER BY AVG(e.salary)
SELECT e, COUNT(p) FROM Employee e LEFT JOIN e.projects p GROUP BY e
HAVING 子句
[edit | edit source]HAVING 子句允許對 GROUP BY 的結果進行過濾。
SELECT AVG(e.salary), e.address.city FROM Employee e GROUP BY e.address.city HAVING AVG(e.salary) > 100000
UNION
[edit | edit source]JPA 不支援 SQL UNION、INTERSECT 和 EXCEPT 操作。某些 JPA 提供商可能支援這些操作。
- EclipseLink : TopLink - 支援 UNION、INTERSECT 和 EXCEPT。
WHERE 子句
[edit | edit source]WHERE 子句通常是查詢的主要部分,因為它定義了過濾返回內容的條件。WHERE 子句可以使用任何比較運算子、邏輯運算子、函式、屬性、物件和子查詢。比較運算子包括 =、<、>、<=、>=、<>、LIKE、BETWEEN、IS NULL 和 IN。NOT 也可與任何比較運算子一起使用 (NOT LIKE、NOT BETWEEN、IS NOT NULL、NOT IN)。邏輯運算子包括 AND、OR 和 NOT。
比較運算子
[edit | edit source]| 操作 | 描述 | 示例 |
|---|---|---|
| = | 等於 | e.firstName = 'Bob'
|
| < | 小於 | e.salary < 100000
|
| > | 大於 | e.salary > :sal
|
| <= | 小於或等於 | e.salary <= 100000
|
| >= | 大於或等於 | e.salary >= :sal
|
| LIKE | 評估兩個字串是否匹配,'%' 和 '_' 是有效的萬用字元,並且 ESCAPE 字元是可選的 | e.firstName LIKE 'A%' OR e.firstName NOT LIKE '%._%' ESCAPE '.'
|
| BETWEEN | 評估該值是否在兩個值之間 | e.firstName BETWEEN 'A' AND 'C'
|
| IS NULL | 將該值與 null 進行比較,資料庫可能不允許或在使用 = 與 null 時產生意外的結果 | e.endDate IS NULL
|
| IN | 評估該值是否包含在列表中 | e.firstName IN ('Bob', 'Fred', 'Joe')
|
IN 操作允許使用值或引數列表、單個列表引數或子查詢。
e.firstName IN (:name1, :name2, :name3)
e.firstName IN (:name1)
e.firstName IN :names
e.firstName IN (SELECT e2.firstName from Employee e2 WHERE e2.lastName = 'Smith')
子查詢可與任何操作一起使用,前提是它返回單個值,或使用 ALL 或 ANY 選項。ALL 表示操作必須對子查詢返回的所有元素都為真,ANY 表示操作必須對子查詢返回的任何元素都為真。
e.firstName = (SELECT e2.firstName from Employee e2 WHERE e2.id = :id)
e.salary < (SELECT e2.salary from Employee e2 WHERE e2.id = :id)
e.firstName = ANY (SELECT e2.firstName from Employee e2 WHERE e.id <> e.id)
e.salary <= ALL (SELECT e2.salary from Employee e2)
更新查詢
[edit | edit source]可以使用UPDATE語句對實體進行批次更新。該語句對單個實體型別進行操作,並設定實體的單個值屬性,這些屬性受WHERE子句中的條件約束。更新查詢提供等效於SQL UPDATE語句的內容,但使用 JPQL 條件表示式。
更新查詢不允許聯接,但支援子查詢。可以在 WHERE 子句中遍歷 OneToOne 和 ManyToOne 關係。仍可以透過在 WHERE 子句中使用包含子查詢的 EXISTS 來查詢集合關係。更新查詢只能更新物件的屬性或其嵌入式屬性,不能更新其關係。複雜更新查詢取決於資料庫的更新支援,並且可能在某些資料庫上使用臨時表。
更新查詢應僅用於批次更新,對物件的常規更新應透過在事務中使用物件的 set 方法並提交更改來完成。
更新查詢返回資料庫中修改的行數(行計數)。
此示例演示如何使用更新查詢為員工加薪。該WHERE子句包含條件表示式。
更新查詢示例
[edit | edit source]Query query = em.createQuery("UPDATE Employee e SET e.salary = 60000 WHERE e.salary = 50000");
int rowCount = query.executeUpdate();
永續性上下文不會更新以反映更新操作的結果。如果使用事務範圍的永續性上下文,則應將批次操作在單獨的事務中執行,或者使其成為事務中的第一個操作。這是因為永續性上下文積極管理的任何實體將不知道資料庫級別發生的實際更改。
與更新查詢匹配的共享快取中的物件將失效,以確保後續永續性上下文看到更新後的資料。
刪除查詢
[edit | edit source]可以使用DELETE語句對實體進行批次刪除。刪除查詢提供等效於SQL DELETE語句的內容,但使用 JPQL 條件表示式。
刪除查詢不允許聯接,但支援子查詢。可以在 WHERE 子句中遍歷 OneToOne 和 ManyToOne 關係。仍可以透過在 WHERE 子句中使用包含子查詢的 EXISTS 來查詢集合關係。複雜刪除查詢取決於資料庫的刪除支援,並且可能在某些資料庫上使用臨時表。
刪除查詢應僅用於批次刪除,對物件的常規刪除應透過呼叫EntityManager remove()API 的動態查詢中。
刪除查詢返回資料庫中刪除的行數(行計數)。
此示例演示如何使用刪除查詢來刪除未分配給部門的所有員工。該WHERE子句包含條件表示式。
刪除查詢示例
[edit | edit source]Query query = em.createQuery("DELETE FROM Employee e WHERE e.department IS NULL");
int rowCount = query.executeUpdate();
刪除查詢是多型的:滿足刪除查詢條件的任何實體子類例項都將被刪除。但是,刪除查詢不遵守級聯規則:除查詢中引用的型別及其子類之外,不會刪除任何其他實體,即使實體與其他具有啟用級聯刪除的實體具有關係也是如此。刪除查詢將從聯接表和集合表中刪除行。
永續性上下文可能不會更新以反映刪除操作的結果。如果使用事務範圍的永續性上下文,則應將批次操作在單獨的事務中執行,或者使其成為事務中的第一個操作。這是因為永續性上下文積極管理的任何實體將不知道資料庫級別發生的實際更改。
引數
[edit | edit source]JPA 定義了命名引數和位置引數。命名引數可以在 JPQL 中使用語法:<name>指定。位置引數可以在 JPQL 中使用語法?或?<position>指定。位置引數從位置1而不是0.
命名引數查詢示例
[edit | edit source]Query query = em.createQuery("SELECT e FROM Employee e WHERE e.firstName = :first and e.lastName = :last");
query.setParameter("first", "Bob");
query.setParameter("last", "Smith");
List<Employee> list = query.getResultList();
位置引數查詢示例
[edit | edit source]Query query = em.createQuery("SELECT e FROM Employee e WHERE e.firstName = ?1 and e.lastName = ?2");
query.setParameter(1, "Bob");
query.setParameter(2, "Smith");
List<Employee> list = query.getResultList();
文字
[edit | edit source]文字值可以在 JPQL 中內聯以用於標準 Java 型別。通常,使用引數而不是內聯值會更好。內聯引數會阻止 JPQL 從 EclipseLink 的 JPQL 解析器快取中受益,並且可能會使應用程式容易受到 JPQL 注入攻擊。
每個 Java 型別都定義了自己的內聯語法
String - '<string>'SELECT e FROM Employee e WHERE e.name = 'Bob'
- 要在一個字串中定義一個'(引號)字元,需要將引號用雙引號括起來,例如:
'Baie-D''Urfé'。
- 要在一個字串中定義一個'(引號)字元,需要將引號用雙引號括起來,例如:
整數 - +|-<數字>SELECT e FROM Employee e WHERE e.id = 1234
長整型 - +|-<數字>LSELECT e FROM Employee e WHERE e.id = 1234L
浮點型 - +|-<數字>.<小數部分><指數>FSELECT s FROM Stat s WHERE s.ratio > 3.14F
雙精度浮點型 - +|-<數字>.<小數部分><指數>DSELECT s FROM Stat s WHERE s.ratio > 3.14e32D
布林型 - TRUE | FALSESELECT e FROM Employee e WHERE e.active = TRUE
日期 - {d'yyyy-mm-dd'}SELECT e FROM Employee e WHERE e.startDate = {d'2012-01-03'}
時間 - {t'hh:mm:ss'}SELECT e FROM Employee e WHERE e.startTime = {t'09:00:00'}
時間戳 - {ts'yyyy-mm-dd hh:mm:ss.nnnnnnnnn'}-SELECT e FROM Employee e WHERE e.version = {ts'2012-01-03 09:00:00.000000001'}
列舉 - 包名.類名.列舉名SELECT e FROM Employee e WHERE e.gender = org.acme.Gender.MALE
空值 - NULLUPDATE Employee e SET e.manager = NULL WHERE e.manager = :manager
JPQL 支援多種資料庫函式。這些函式在名稱和語法上獨立於資料庫,但需要資料庫支援。如果資料庫支援等效的函式或不同的語法,則支援標準 JPQL 函式;如果資料庫沒有提供任何執行該函式的方法,則不支援該函式。對於數學函式(+、-、/、*),遵循 BEDMAS 規則。一些 JPA 提供者可能支援其他函式。
- EclipseLink : TopLink - 也支援 CAST、EXTRACT 和 REGEXP。
| 函式 | 描述 | 示例 |
|---|---|---|
| - | 減法 | e.salary - 1000
|
| + | 加法 | e.salary + 1000
|
| * | 乘法 | e.salary * 2
|
| / | 除法 | e.salary / 2
|
| ABS | 絕對值 | ABS(e.salary - e.manager.salary)
|
| CASE | 定義一個 case 語句 | CASE e.status WHEN 0 THEN 'active' WHEN 1 THEN 'consultant' ELSE 'unknown' END
|
| COALESCE | 評估為第一個非空引數值 | COALESCE(e.salary, 0)
|
| CONCAT | 連線兩個或多個字串值 | CONCAT(e.firstName, ' ', e.lastName)
|
| CURRENT_DATE | 資料庫上的當前日期 | CURRENT_DATE
|
| CURRENT_TIME | 資料庫上的當前時間 | CURRENT_TIME
|
| CURRENT_TIMESTAMP | 資料庫上的當前日期時間 | CURRENT_TIMESTAMP
|
| LENGTH | 字元或二進位制值的字元/位元組長度 | LENGTH(e.lastName)
|
| LOCATE | 字串在字串中的索引,可以選擇從起始索引開始 | LOCATE('-', e.lastName)
|
| LOWER | 將字串值轉換為小寫 | LOWER(e.lastName)
|
| MOD | 計算第一個整數除以第二個整數的餘數 | MOD(e.hoursWorked / 8)
|
| NULLIF | 如果第一個引數等於第二個引數,則返回 null,否則返回第一個引數 | NULLIF(e.salary, 0)
|
| SQRT | 計算數字的平方根 | SQRT(o.result)
|
| SUBSTRING | 從字串中獲取子字串,從索引開始,可以選擇子字串大小 | SUBSTRING(e.lastName, 0, 2)
|
| TRIM | 從字串中修剪前導、尾隨或同時修剪空格或可選修剪字元 | TRIM(TRAILING FROM e.lastName), TRIM(e.lastName), TRIM(LEADING '-' FROM e.lastName)
|
| UPPER | 將字串值轉換為大寫 | UPPER(e.lastName)
|
JPQL 定義了幾個特殊的運算子,它們不是資料庫函式,但在 JPQL 中具有特殊含義。這些包括 INDEX、KEY、SIZE、IS EMPTY、TYPE、FUNCTION 和 TREAT。
- EclipseLink : TopLink - 也支援 FUNCTION、TREAT、FUNC、OPERATOR、SQL、COLUMN 和 TABLE。
| 函式 | 描述 | 示例 |
|---|---|---|
| INDEX | 有序列表元素的索引,僅支援 @OrderColumn | SELECT toDo FROM Employee e JOIN e.toDoList toDo WHERE INDEX(toDo) = 1
|
| KEY | Map 元素的鍵 | SELECT p FROM Employee e JOIN e.priorities p WHERE KEY(p) = 'high'
|
| SIZE | 集合關係的大小,這將評估為一個子查詢 | SELECT e FROM Employee e WHERE SIZE(e.managedEmployees) < 2
|
| IS EMPTY, IS NOT EMPTY | 如果集合關係為空,則評估為 true,這將評估為一個子查詢 | SELECT e FROM Employee e WHERE e.managedEmployees IS EMPTY
|
| MEMBER OF, NOT MEMBER OF | 如果集合關係包含該值,則評估為 true,這將評估為一個子查詢 | SELECT e FROM Employee e WHERE 'write code' MEMBER OF e.responsibilities
|
| TYPE | 繼承鑑別器值 | SELECT p FROM Project p WHERE TYPE(p) = LargeProject
|
| TREAT | 將物件視為其子類值(JPA 2.1 草案) | SELECT e FROM Employee JOIN TREAT(e.projects as LargeProject) p WHERE p.budget > 1000000
|
| FUNCTION | 呼叫資料庫函式(JPA 2.1 草案) | SELECT p FROM Phone p WHERE FUNCTION('TO_NUMBER', p.areaCode) > 613
|