Java 永續性/查詢
查詢是永續性的基本組成部分。如果無法查詢資料,那麼持久化資料就沒有什麼用處。有很多查詢語言和框架;最常見的查詢語言是關係型資料庫中使用的 SQL。
JPA 提供了幾種查詢機制
- JPQL : (BNF)
- Criteria API
- 本地 SQL 查詢
JPA 主要使用 Java 永續性查詢語言 (JPQL),該語言基於 SQL 語言,並從 EJB 查詢語言 (EJBQL) 演變而來。它基本上在物件級別而不是資料級別提供 SQL 語法。JPQL 在語法上類似於 SQL,可以透過其BNF 定義來定義。
JPA 還提供 Criteria API,允許使用 Java API 輕鬆構建動態查詢。Criteria API 映象 JPQL 語法,但為每個操作/函式提供 Java API,而不是使用單獨的查詢語言。
JPA 透過 Query 介面和 @NamedQuery 和 @NamedNativeQuery 註解以及 <named-query> 和 <named-native-query> XML 元素提供查詢功能。
其他查詢語言和框架包括
- SQL
- EJBQL
- JDOQL
- EQL (EclipseLink 查詢語言)
- 按示例查詢 (QBE)
- TopLink 表示式
- Hibernate Criteria
- 物件查詢語言 (OQL)
- 查詢 DSL
JPA 中主要有兩種型別的查詢:命名查詢和動態查詢。命名查詢用於應用程式中多次使用的靜態查詢。命名查詢的優點是它可以在一個地方定義一次,並在應用程式中重複使用。大多數 JPA 提供程式還會預解析/編譯命名查詢,因此它們比動態查詢更最佳化,動態查詢通常必須在每次執行時解析/編譯。由於命名查詢是永續性元資料的組成部分,因此它們也可以在 orm.xml 中進行最佳化或覆蓋,而無需更改應用程式程式碼。
命名查詢透過 @NamedQuery 和 @NamedQueries 註解或 <named-query> XML 元素來定義。命名查詢透過 EntityManager.createNamedQuery API 訪問,並透過 Query 介面執行。
命名查詢可以在任何帶註解的類上定義,但通常定義在它們查詢的 Entity 上。命名查詢的名稱對於整個永續性單元必須是唯一的,名稱不是 Entity 本地的。在 orm.xml 中,命名查詢可以在 <entity-mappings> 或任何 <entity> 上定義。
命名查詢通常是引數化的,因此它們可以使用不同的引數值執行。引數在 JPQL 中使用 :<name> 語法來定義命名引數,或使用 ? 語法來定義位置引數。
還可以向命名查詢提供查詢提示集合。查詢提示可用於最佳化或向查詢提供特殊配置。查詢提示特定於 JPA 提供程式。查詢提示透過 @QueryHint 註解或 query-hint XML 元素來定義。
@NamedQuery(
name="findAllEmployeesInCity",
query="Select emp from Employee emp where emp.address.city = :city"
hints={@QueryHint(name="acme.jpa.batch", value="emp.address")}
)
public class Employee {
...
}
<entity-mappings>
<entity name="Employee" class="org.acme.Employee" access="FIELD">
<named-query name="findAllEmployeesInCity">
<query>Select emp from Employee emp where emp.address.city = :city</query>
<hint name="acme.jpa.batch" value="emp.address"/>
</named-query>
<attributes>
<id name="id"/>
</attributes>
</entity>
</entity-mappings>
EntityManager em = getEntityManager();
Query query = em.createNamedQuery("findAllEmployeesInCity");
query.setParameter("city", "Ottawa");
List<Employee> employees = query.getResultList();
...
動態查詢通常用於查詢取決於上下文的情況。例如,取決於查詢表單中哪些專案已填寫,查詢可能具有不同的引數。動態查詢對於不常見查詢或原型設計也很有用。
JPA 為動態查詢提供了兩種主要選擇:JPQL 和 Criteria API。
動態查詢可以使用引數和查詢提示,與命名查詢相同。
動態查詢透過 EntityManager.createQuery API 訪問,並透過 Query 介面執行。
EntityManager em = getEntityManager();
Query query = em.createQuery("Select emp from Employee emp where emp.address.city = :city");
query.setParameter("city", "Ottawa");
query.setHint("acme.jpa.batch", "emp.address");
List<Employee> employees = query.getResultList();
...
參見 Criteria API.
JPQL
[edit | edit source]參見JPQL.
引數
[edit | edit source]引數在 JPQL 中使用 :<param> 語法定義,例如 "Select e from Employee e where e.id = :id"。引數值使用 Query 的 Query.setParameter API 設定。
引數也可以使用 ? 定義,主要用於原生 SQL 查詢。你也可以使用 ?<int>。這些是位置引數,而不是命名引數,使用 Query API Query.setParameter 設定。int 是引數在 SQL 中的索引。位置引數從 1 開始(不是 0)。一些 JPA 提供程式也允許在原生查詢中使用 :<param> 語法。
對於時間引數 (Date, Calendar),你也可以傳遞時間型別,具體取決於你是否要從值中獲取 Date, Time 或 Timestamp。
引數通常是基本值,但你也可以引用物件,如果它們在 Id 上進行比較,例如,"Select e from Employee e where e.address = :address" 可以使用 Address 物件作為引數。引數值在與對映屬性比較時始終在物件級別,例如,如果與對映的 enum 進行比較,則使用 enum 值,而不是資料庫值。
引數始終在 Query 上設定,無論它是什麼型別的查詢(JPQL、Criteria、原生 SQL、NamedQuery)。
命名引數
Query query = em.createQuery("Select e from Employee e where e.name = :name");
query.setParameter("name", "Bob Smith");
位置引數
Query query = em.createNativeQuery("SELECT * FROM EMPLOYEE WHERE NAME = ?");
query.setParameter(1, "Bob Smith");
查詢結果
[edit | edit source]通常,JPA 查詢返回你的持久 Entity 物件。返回的物件將由持久上下文 (EntityManager) 管理,對物件的更改將作為當前事務的一部分進行跟蹤。在某些情況下,可以構建更復雜的查詢,這些查詢只返回資料而不是 Entity 物件,甚至執行 update 或 deletion 操作。
有三種方法可以執行 Query,每種方法都返回不同的結果
getResultList 返回結果的 List。這通常是 Entity 物件的 List,但也可能是資料的列表或陣列。
| JPQL / SQL | 結果 |
SELECT e FROM Employee e | 這將返回一個 List<Employee>(Employee 物件的列表)。這些物件將被管理。 |
SELECT e.firstName FROM Employee e | 這將返回一個 List<String>(String 值的列表)。資料未被管理。 |
SELECT e.firstName, e.lastName FROM Employee e | 這將返回一個 List<Object[String, String]>(每個包含兩個 String 值的物件陣列的列表)。資料未被管理。 |
SELECT e, e.address FROM Employee e | 這將返回一個 List<Object[Employee, Address]>(每個包含 Employee 和 Address 物件的物件陣列的列表)。這些物件將被管理。 |
SELECT EMP_ID, F_NAME, L_NAME FROM EMP | 這將返回一個 List<Object[BigDecimal, String, String]>(每個包含行資料的物件陣列的列表)。資料未被管理。 |
getSingleResult 返回結果。這通常是一個 Entity 物件,但也可能是資料或物件陣列。如果查詢沒有返回任何結果,則會丟擲異常。這很不幸,因為通常只是返回 null 是想要的。一些 JPA 提供程式可能提供一個選項,如果未返回任何結果,則返回 null 而不是丟擲異常。如果查詢返回的不僅僅是一行,也會丟擲異常。這也很不幸,因為通常只是返回第一個結果是想要的。一些 JPA 提供程式可能提供一個選項,返回第一個結果而不是丟擲異常,否則你需要呼叫 getResultList 並獲取第一個元素。
| JPQL / SQL | 結果 |
SELECT e FROM Employee e | 這將返回一個 Employee。該物件將被管理。 |
SELECT e.firstName FROM Employee e | 這將返回一個 String。資料未被管理。 |
SELECT e.firstName, e.lastName FROM Employee e | 這將返回一個 Object[String, String](包含兩個 String 值的物件陣列)。資料未被管理。 |
SELECT e, e.address FROM Employee e | 這將返回一個 Object[Employee, Address](包含 Employee 和 Address 物件的物件陣列)。這些物件將被管理。 |
SELECT EMP_ID, F_NAME, L_NAME FROM EMP | 這將返回一個 Object[BigDecimal, String, String](包含行資料的物件陣列)。資料未被管理。 |
executeUpdate 返回資料庫行計數。這可用於 UPDATE DELETE JPQL 查詢,或任何不返回結果的原生 SQL (DML 或 DDL) 查詢。
常用查詢
[edit | edit source]連線,查詢一對多關係
[edit | edit source]要查詢 所有電話號碼區號為 613 的員工,需要使用 連線。
JPQL
SELECT e FROM Employee e JOIN e.phoneNumbers p where p.areaCode = '613'
Criteria
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Employee> query = cb.createQuery(Employee.class);
Root<Employee> employee = query.from(Employee.class);
Join<PhoneNumber> phone = employee.join("phoneNumbers");
query.where(cb.equal(phone.get("areaCode"), "613"));
子查詢,查詢多對多關係中的所有關係
[edit | edit source]要查詢 所有專案都處於困難狀態的員工,需要使用帶有雙重否定的 子查詢。
JPQL
SELECT e FROM Employee e JOIN e.projects p where NOT EXISTS (SELECT t from Project t where p = t AND t.status <> 'In trouble')
Criteria
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Employee> query = cb.createQuery(Employee.class);
Root<Employee> employee = query.from(Employee.class);
Join<Project> project = employee.join("projects");
Subquery<Project> subquery = query.subquery(Project.class);
Root<Project> subProject = query.from(Project.class);
subquery.where(cb.and(cb.equal(project, subProject), cb.equal(subProject.get("status"), "In trouble")));
query.where(cb.not(cb.exists(subquery));
子查詢,查詢多對多關係,其中所有關係都在列表中
[edit | edit source]要查詢 所有參與所有專案列表的員工,需要使用帶有計數的 子查詢。首先從專案中收集專案 ID(使用物件也可以)。然後獲取列表的大小。你的查詢將需要兩個引數,一個是 ID 列表,另一個是列表的大小。
JPQL
SELECT e FROM Employee e where :size = (SELECT COUNT(p) from Project p, Employee e2 join e2.projects p2 where p = p2 AND e = e2 AND p.id IN :projects)
連接獲取,在同一查詢中讀取員工和地址
[edit | edit source]要查詢 所有員工及其地址,需要使用 連接獲取。這將在同一查詢中選擇員工和地址資料。如果沒有使用連接獲取,則員工地址仍然可用,但可能會導致對每個員工及其地址的查詢。這將 n+1 個查詢減少到 1 個查詢。
連接獲取
SELECT e FROM Employee e JOIN FETCH e.address
連接獲取也可以用於集合關係
SELECT e FROM Employee e JOIN FETCH e.address JOIN FETCH e.phones
外連線可用於避免 null 和空關係過濾結果
SELECT e FROM Employee e LEFT OUTER JOIN FETCH e.address LEFT OUTER JOIN FETCH e.phones
你也可以在一個查詢中選擇多個物件,但請注意,這不會例項化關係,因此訪問關係仍然可能觸發另一個查詢
SELECT e, a FROM Employee e, Address a WHERE e.address = a
反向多對多,給定專案的全部員工
[edit | edit source]要查詢 給定專案的全部員工,其中員工專案關係為多對多。
如果關係是雙向的,你可以使用
Select p.employees from Project p where p.name = :name
如果它是單向的,你可以使用
Select e from Employee e, Project p where p.name = :name and p member of e.projects
或者
Select e from Employee e join e.projects p where p.name = :name
如何模擬強制轉換為子類
[edit | edit source]要查詢 所有擁有預算超過 1,000,000 的大型專案的員工,其中員工只與 Project 有關係,而不是與 LargeProject 子類有關係。JPA 1.0 JPQL 沒有定義強制轉換操作(JPA 2.0 可能定義了它),因此在子類的屬性上進行查詢並不明顯。但是,如果將子類的二級連線新增到查詢中,則可以間接完成此操作。
Select e from Employee e join e.projects p, LargeProject lproject where p = lproject and lproject.budget > 1000000
如何選擇集合中的第一個元素
[edit | edit source]要查詢 特定員工的第一個專案。有幾種不同的方法可以做到這一點,有些方法使用直接的 JPQL,有些方法使用 Query 的 setMaxResuls API。如果 JPA 2.0 索引列表用於對映集合,則可以使用 INDEX 函式。
setMaxResults
Query query = em.createQuery("Select e.projects from Employee e where e.id = :id");
query.setMaxResults(1);
Query query = em.createQuery("Select p from Employee e join e.projects p where e.id = :id");
query.setMaxResults(1);
JPQL
Select p from Project p where p.id = (Select MAX(p2.id) from Employee e join e.projects p2 where e.id = :id)
JPA 2.0
Select p from Employee e join e.projects p where e.id = :id and INDEX(p) = 1
如何按集合的大小排序
[edit | edit source]要查詢按專案數量排序的所有員工。 有幾種不同的方法可以做到這一點,一些方法最終在 SQL 中使用子查詢,另一些方法使用 group by。 具體取決於您的 JPA 提供程式和資料庫,您的解決方案可能僅限於其中之一。
使用 SIZE 函式(在 SQL 中使用子查詢)
Select e from Employee order by SIZE(e.projects) DESC
使用 SIZE 函式,也選擇大小(使用 group by)
Select e, SIZE(e.projects) from Employee order by SIZE(e.projects) DESC
使用 GROUP BY
Select e, COUNT(p) from Employee join e.projects p order by COUNT(p) DESC
使用 GROUP BY 和別名
Select e, COUNT(p) as pcount from Employee join e.projects p order by pcount DESC
在 JPA 中有幾種最佳化查詢的方法。 典型的查詢效能問題是首先讀取物件,然後逐個讀取其相關物件。 這可以使用 JPQL 中的JOIN FETCH進行最佳化,否則使用特定於每個 JPA 提供程式的查詢提示進行最佳化。
參見:
執行查詢時可以使用多個 JDBC 選項。 這些 JDBC 選項不會由 JPA 公開,但一些 JPA 提供程式可能支援針對它們的查詢提示。
- 獲取大小:配置從資料庫中每頁獲取的行數。 對於大型查詢,更大的獲取大小效率更高。
- 超時:指示資料庫在查詢執行時間過長時取消查詢。
- EclipseLink/TopLink:提供許多查詢提示,包括
- "eclipselink.jdbc.fetch-size" - 獲取大小。
- "eclipselink.jdbc.timeout" - 超時。
- "eclipselink.read-only" - 從查詢返回的物件不受持久上下文管理,也不跟蹤更改。
- "eclipselink.query-type" - 定義要用於查詢的查詢的本機型別。
- "eclipselink.sql.hint" - 允許在查詢的 SQL 中包含 SQL 提示。
- "eclipselink.jdbc.bind-parameters" - 指定是否應使用引數繫結(預設情況下使用)。
JPQL 還允許執行UPDATE和DELETE查詢。 這不是在 JPA 中修改物件的推薦或正常方式。 通常在 JPA 中,您首先讀取物件,然後直接使用其set方法修改它以更新它,或者呼叫EntityManager.remove()方法刪除它。
JPQL 中的UPDATE和DELETE查詢用於執行批次更新或刪除。 它們允許在單個查詢中更新或刪除一組物件。 這些查詢對於執行批次操作或清除測試資料很有用。
UPDATE和DELETE查詢與SELECT查詢具有相同的WHERE,可以使用相同的函式和操作,並遍歷關係並使用子查詢。 UPDATE和DELETE查詢使用Query.executeUpdate()方法執行,並返回來自資料庫的行數。 請注意,在活動持久上下文中執行這些查詢時應謹慎使用,因為查詢可能會影響已在EntityManager中註冊的物件。 通常最好在執行查詢後clear()EntityManager,或者在新的EntityManager或事務中執行查詢。
UPDATE Employee e SET e.salary = e.salary + 1000 WHERE e.address.city = :city
DELETE FROM Employee e WHERE e.address.city = :city
在 JPA 中的事務上下文中,對受管物件的更改通常不會在提交之前重新整理(寫入)到資料庫。 因此,如果直接針對資料庫執行查詢,它將看不到事務中進行的更改,因為這些更改僅在 Java 中的記憶體中進行。 如果已持久化新物件,或者物件已被刪除或更改,這可能會導致問題,因為應用程式可能希望查詢返回這些結果。 由於這種原因,JPA 要求 JPA 提供程式在任何查詢操作之前執行對資料庫的所有更改的重新整理。 但是,如果應用程式沒有期望重新整理作為查詢操作的副作用,這可能會導致問題。 重新整理也可能很昂貴,並且會導致資料庫事務以及資料庫鎖和其他資源在事務持續時間內被持有,這會影響效能和併發。
JPA 允許使用FlushModeType列舉和Query.setFlushMode()API 配置查詢的重新整理模式。 重新整理模式是AUTO(預設值,表示在每次查詢執行之前重新整理)或COMMIT(表示僅在提交時重新整理)。 也可以使用EntityManager.setFlushMode()API 在EntityManager上設定重新整理模式,以影響使用EntityManager執行的所有查詢。 可以隨時在EntityManager上直接呼叫EntityManager.flush()API 以執行所需重新整理。
一些 JPA 提供程式還允許透過永續性單元屬性配置重新整理模式,或提供重新整理替代方案,例如針對記憶體中物件執行查詢。
- TopLink / EclipseLink:允許使用永續性單元屬性
"eclipselink.persistence-context.flush-mode"="COMMIT"停用自動重新整理。
- TopLink / EclipseLink:允許使用永續性單元屬性
一個常見的需求是允許使用者瀏覽大型查詢結果。 通常,在查詢執行後,Web 使用者會獲得前n條結果的第一頁,並且可以點選下一頁轉到下一頁,或點選上一頁返回。
如果您不關心效能,或者結果不太大,實現此功能的最簡單方法是查詢所有結果,然後從結果列表中訪問子列表以填充您的頁面。 但是,您將不得不每次頁面請求都重新查詢所有結果。
一個簡單的解決方案是將查詢結果儲存在有狀態的SessionBean或http 會話中。 這意味著初始查詢可能需要一段時間,但分頁會很快。 一些 JPA 提供程式還支援查詢結果的快取,因此您可以將結果快取到 JPA 提供程式的快取中,只需重新執行查詢即可獲得快取的結果。
如果查詢結果非常大,則可能需要其他解決方案。 JPA 提供了Query API setFirstResult、setMaxResults,以允許瀏覽大型查詢結果。 maxResults也可以用作保障措施,以避免讓使用者執行返回太多物件的查詢。
這些查詢屬性的實現方式取決於 JPA 提供程式和資料庫。 JDBC 允許設定maxResults,並且大多數 JDBC 驅動程式都支援此功能,因此它通常適用於大多數 JPA 提供程式和大多數資料庫。 firstResult的支援效率可能無法得到保證,因為它通常需要資料庫特定的 SQL。 沒有用於分頁的標準 SQL,因此是否支援它取決於您的資料庫以及 JPA 提供程式的支援。
執行分頁時,還必須對結果進行排序。 如果查詢沒有對結果進行排序,則每個後續查詢都有可能以不同的順序返回結果,並提供不同的頁面。 此外,如果在查詢之間插入/刪除行,則結果可能略有不同。
使用 firstResult、maxResults 的示例
[edit | edit source]Query query = em.createQuery("Select e from Employee e order by e.id");
query.setFirstResult(100);
query.setMaxResults(200);
List<Employee> page = query.getResultList();
除了使用firstResult,您還可以根據排序規則和上一頁的值在 where 子句中過濾第一個結果。
使用 maxResults 和 order by 的示例
[edit | edit source]Query query = em.createQuery("Select e from Employee e where e.id > :lastId order by e.id");
query.setParameter("lastId", previousPage.get(previousPage.size()-1).getId());
query.setMaxResults(100);
List<Employee> nextPage = query.getResultList();
另一種方法是隻查詢Id,並將結果儲存在狀態化SessionBean或http session中。然後,您可以針對每頁查詢一組Id。
使用 Ids 和 IN 的示例
[edit | edit source]Query query = em.createQuery("Select e.id from Employee e");
List<Long> ids= query.getResultList();
Query pageQuery = em.createQuery("Select e from Employee e where e.id in :ids");
pageQuery.setParameter("ids", ids.subList(100, 200));
List<Employee> page = pageQuery.getResultList();
分頁還可以用於伺服器程序或批處理作業。在伺服器上,通常使用分頁來避免預先使用太多記憶體,並允許一次處理一個批次。您可以使用任何這些技術,一些 JPA 提供程式還支援返回資料庫遊標來進行查詢結果,允許您滾動瀏覽結果。
- TopLink / EclipseLink : 透過查詢提示
"eclipselink.cursor.scrollable"和"eclipselink.cursor"以及CursoredStream和ScrollableCursor類支援流和可滾動遊標。
- TopLink / EclipseLink : 透過查詢提示
本地 SQL 查詢
[edit | edit source]通常,JPA 中的查詢是透過 JPQL 定義的。JPQL 允許您根據物件模型而不是資料模型來定義查詢。由於開發人員使用物件模型在 Java 中程式設計,因此這通常更直觀。這也允許資料抽象以及資料庫架構和資料庫平臺獨立性。JPQL 支援大部分 SQL 語法,但某些方面的 SQL 或者特定的資料庫擴充套件或函式可能無法透過 JPQL 實現,因此有時需要使用原生 SQL 查詢。此外,一些開發人員對 SQL 比對 JPQL 更熟悉,因此可能更喜歡使用 SQL 查詢。原生查詢還可以用於呼叫某些型別的儲存過程或執行 DML 或 DDL 操作。
原生查詢是透過 @NamedNativeQuery 和 @NamedNativeQueries 註解或 <named-native-query> XML 元素來定義的。還可以使用 EntityManager.createNativeQuery() API 動態定義原生查詢。
原生查詢可以用於查詢類例項、查詢原始資料、更新或 DML 或 DDL 操作,或查詢複雜查詢結果。如果查詢針對的是類,則必須設定查詢的 resultClass 屬性。如果查詢結果很複雜,則可以使用 結果集對映。
原生查詢可以引數化,因此可以使用不同的引數值執行。引數是在 SQL 中使用 ? 語法來定義位置引數的,JPA 不要求原生查詢支援命名引數,但一些 JPA 提供程式可能支援。對於位置引數,位置從 1 開始(而不是 0)。
還可以為原生查詢提供一組查詢提示。查詢提示可以用於最佳化或為查詢提供特殊配置。查詢提示特定於 JPA 提供程式。查詢提示是透過 @QueryHint 註解或 query-hint XML 元素來定義的。
原生命名查詢註解示例
[edit | edit source]@NamedNativeQuery(
name="findAllEmployeesInCity",
query="SELECT E.* from EMP E, ADDRESS A WHERE E.EMP_ID = A.EMP_ID AND A.CITY = ?",
resultClass=Employee.class
)
public class Employee {
...
}
原生命名查詢 XML 示例
[edit | edit source]<entity-mappings>
<entity name="Employee" class="org.acme.Employee" access="FIELD">
<named-native-query name="findAllEmployeesInCity" result-class="org.acme.Employee">
<query>SELECT E.* from EMP E, ADDRESS A WHERE E.EMP_ID = A.EMP_ID AND A.CITY = ?</query>
</named-native-query>
<attributes>
<id name="id"/>
</attributes>
</entity>
</entity-mappings>
原生命名查詢執行示例
[edit | edit source]EntityManager em = getEntityManager();
Query query = em.createNamedQuery("findAllEmployeesInCity");
query.setParameter(1, "Ottawa");
List<Employee> employees = query.getResultList();
...
動態原生查詢執行示例
[edit | edit source]EntityManager em = getEntityManager();
Query query = em.createNativeQuery("SELECT E.* from EMP E, ADDRESS A WHERE E.EMP_ID = A.EMP_ID AND A.CITY = ?", Employee.class);
query.setParameter(1, "Ottawa");
List<Employee> employees = query.getResultList();
...
結果集對映
[edit | edit source]當原生 SQL 查詢返回物件時,SQL 必須確保它返回正確的資料,以使用對映中指定的正確列名來構建 resultClass。如果 SQL 更復雜並返回不同的列名或返回多個物件的資料,則必須使用 @SqlResultSetMapping。
@SqlResultSetMapping 是一個相當複雜的註解,它包含一個 @EntityResult、@ConstructorResult 和 @ColumnResult 陣列。這允許將多個 Entity 物件與原始資料和未對映的類組合返回。@EntityResult 包含一個 @FieldResult 陣列,它可以用於將 SQL 中使用的別名對映到對映所需的列名。如果需要返回同一個類的兩個不同例項,或者如果 SQL 需要出於某種原因以不同的方式對列進行別名,則需要這樣做。請注意,在 @FieldResult 中,name 是物件中屬性的名稱,而不是對映中的列名。這似乎很奇怪,因為這將使對映 Embedded 或組合 id 關係變得不可能。
通常,使用原生 SQL 查詢選擇原始資料或單個物件最容易,因此通常可以避免 @SqlResultSetMapping,因為它們相當複雜。另外請注意,即使您使用 SQL 選擇 Employee 及其 Address,它們也是兩個不相關的物件,員工的地址不會被設定,並且可能會觸發查詢,除非快取命中。一些 JPA 提供程式可能會提供一個查詢提示,以允許連接獲取與原生 SQL 查詢一起使用。
- TopLink / EclipseLink : 透過
"eclipselink.join-fetch"查詢提示支援原生 SQL 查詢的連接獲取。
- TopLink / EclipseLink : 透過
結果集對映註解示例
[edit | edit source]@NamedNativeQuery(
name="findAllEmployeesInCity",
query="SELECT E.*, A.* from EMP E, ADDRESS A WHERE E.EMP_ID = A.EMP_ID AND A.CITY = ?",
resultSetMapping="employee-address"
)
@SqlResultSetMapping(name="employee-address",
entities={
@EntityResult(entityClass=Employee.class),
@EntityResult(entityClass=Address.class)}
)
public class Employee {
...
}
ConstructorResult (JPA 2.1)
[edit | edit source]JPA 2.1 定義了一個 @ConstructorResult 註解,以允許從原生 SQL 查詢中返回未對映的類。ConstructorResult 類似於 JPQL NEW 運算子,它允許呼叫類建構函式並傳遞原始資料。ConstructorResult 具有一個 targetClass 和一個 columns 陣列,該陣列包含 ColumnResults。目標類必須定義一個建構函式,該建構函式接受與 columns 定義的相同數量的引數和型別。
建構函式結果註解示例
[edit | edit source]@NamedNativeQuery(
name="findAllEmployeeDetails",
query="SELECT E.EMP_ID, E.F_NAME, E.L_NAME, A.CITY from EMP E, ADDRESS A WHERE E.EMP_ID = A.EMP_ID",
resultSetMapping="employee-details"
)
@SqlResultSetMapping(name="employee-details",
classes={
@ConstructorResult(targetClass=EmployeeDetails.class, columns={
@ColumnResult(name="EMP_ID", type=Integer.class),
@ColumnResult(name="F_NAME", type=String.class),
@ColumnResult(name="L_NAME", type=String.class),
@ColumnResult(name="CITY", type=String.class)
})
}
)
public class Employee {
...
}
儲存過程
[edit | edit source]參見 儲存過程
原始 JDBC
[edit | edit source]有時可能需要將 JDBC 程式碼與 JPA 程式碼混合使用。這可能是為了訪問某些 JDBC 驅動程式特定功能,或為了與使用 JDBC 而不是 JPA 的另一個應用程式整合。
如果您只需要 JDBC 連線,您可以從 JEE 伺服器的 DataSource 訪問連線,或者直接連線到 DriverManager 或第三方連線池。如果您在同一個事務上下文中需要 JDBC 連線和 JPA 應用程式,則可以使用 JTA DataSource 用於 JPA 和您的 JDBC 訪問,以使它們共享同一個全域性事務。如果您沒有使用 JEE 或沒有使用 JTA,那麼您可能能夠直接從 JPA 提供程式訪問 JDBC 連線。
一些 JPA 提供程式提供了一個 API,用於從其內部連線池或其事務上下文訪問原始 JDBC 連線。在 JPA 2.0 中,這個 API 在 EntityManager 上的 unwrap API 中有所標準化。
要從 EntityManager 訪問 JDBC 連線,一些 JPA 2.0 提供程式可能支援
java.sql.Connection connection = entityManager.unwrap(java.sql.Connection.class);
然後可以將此連線用於原始 JDBC 訪問。通常在完成時不應關閉它,因為連線正在被 EntityManager 使用,並且將在 EntityManager 關閉或事務提交時釋放。
- TopLink / EclipseLink : 支援解包 JDBC
Connection。
- TopLink / EclipseLink : 支援解包 JDBC