JPA支援兩種表達查詢的方法來檢索實體和來自資料庫的其他持久化資料:查詢語句(Java Persistence Query Language,JPQL)和條件API(criteria API)。JPQL是獨立於資料庫的查詢語句,其用於操作邏輯上的實體模型而非物理的資料模型。條件API是根據實體模型構建查詢條件
1.Java持久化查詢語句入門 List persons= entityManager.createQuery("select p from Person p").getResultList();
1.這個查詢語句類似於SQL。但它與真正的SQL的區別是,它不是從一個表中進行選擇查詢,而是指定來自應用程式域模型的實體。 2.查詢select子句也只是列出了查詢實體的別名,如果只查詢某一列的,可以使用點(.)操作符進行來導航實體屬性。如下所示: List persons= entityManager.createQuery("select p.firstName from Person p").getResultList();
1.1.篩選條件 像SQL一樣,JPQL也支援where子句,用於對搜尋的條件過濾。包括大多數的操作符,如:in,between、like以及函式表示式substring、length等等 List persons = entityManager.createQuery("select p from Person p where p.age>23").getResultList();
1.2.投影結果 對於查詢的資料量比較大的話,可以使用投影的方式,只查詢出有用的列。 //投影 List persons = entityManager.createQuery("select p.firstName,p.age from Person p").getResultList();
1.3.聚合查詢 JPQL的聚合查詢語法類似於SQL。例如count List count=entityManager.createQuery("select count(p) from Person p").getResultList();
1.4.查詢引數 JPQL支援兩種型別的引數繫結語法。
1.位置參數列示法
其中引數是在查詢字串中指示,該字串是在一個問號(?)之後緊隨引數的編號。當執行查詢的時候,開發人員指定應該替換的引數編
Query query=entityManager.createQuery("select p from Person p where p.age=?1 and p.firstName=?2"); query.setParameter(1,21); query.setParameter(2,"Jack");
2.命名參數列示法
通過在一個冒號(:)之後緊隨引數名稱,在查詢字串對它進行指示,當執行查詢的時候,開發人員指定應該替換的引數名稱
Query query=entityManager.createQuery("select p from Person p where p.age=:age and p.firstName=:name"); query.setParameter("age",21); query.setParameter("name","Jack");
2.定義查詢 JPA提供Query和TypedQuery(JPA 2.0引入)介面來配置和執行查詢。Query的返回的Object型別,而TypedQuery返回的是指定的Class型別。 //未指定型別,返回Object型別 Query q = entityManager.createQuery("select p from Person p"); //指定返回型別為Person型別 TypedQuery q1 = entityManager.createQuery("select p from Person p", Person.class);
2.1.動態查詢定義 JPA查詢引擎,可以將JPQL字串解析成語法樹,獲取表示式中的實體物件-關係對映的後設資料,然後生成等價的SQL。故有兩種方式進行動態查詢。
1.拼接字串方式
Tip:會引起SQL隱碼攻擊問題
/**
- 動態拼接字串構建查詢條件
- @param name
- @param age
- @return */ public static String queryPersonJPQL(String name, int age) { String queryQL = "select p from Person p where p.firstName= '" + name + "' and p.age=" + age; return queryQL; }
//呼叫 Query query = entityManager.createQuery(queryPersonJPQL("jack", 21));
2.動態引數化構建查詢條件(推薦使用)
/**
- 動態引數化構建查詢條件
- @return */ public static String queryPersonJPQLByParams() { String queryQL = "select p from Person p where p.firstName=:name and p.age=:age"; return queryQL; }
Query query = entityManager.createQuery(queryPersonJPQLByParams()); query.setParameter("name", "Jack"); query.setParameter("age", 21);
2.2.命名查詢定義 命名查詢是一個強大的工具。使用@NamedQuery註解定義一個命名查詢,可以把它放在任何實體的類定義之上。該註解定義了查詢的名稱,及其查詢的文字。
Tip:命名查詢通暢放置在對應查詢結果的實體類上
@Entity @NamedQuery(name = "findByAge", query = "select p from Person p where p.age=:age") public class Person { //省略 }
Tip:NamedQuery裡面定義的名稱在整個持久化單元中需要唯一,不然執行會出錯。
eg: Exception in thread "main" org.hibernate.DuplicateMappingException: Duplicate query mapping findByAge at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.checkQueryName
呼叫
List people = entityManager.createNamedQuery("findByAge", Person.class).setParameter("age", 21).getResultList();
如果一個類定義兩個或者以上個的命名查詢,那麼必須把它放置在@NamedQueries()
2.3.繫結引數 通過前面的例子,我們可以看到繫結引數有兩種方式:1.位置引數化繫結。2.命名引數化繫結。都是通過Query介面的setParameter方法進行繫結。
1.位置引數化
TypedQuery setParameter(int position, Object value);
2.命名引數化
TypedQuery setParameter(String name, Object value);
第一種位置引數化繫結,如果位置發生變化都需要改變繫結的程式碼。推薦使用第二種。
2.4.執行查詢 Query介面與TypedQuery介面提供了三種不同的方式執行查詢。
1.executeUpdate
用來執行批量更新或者刪除
2.getSingleResult
獲取單個結果集。如果沒有獲取到資料,則會丟擲NoResultException異常。如果獲取多條資料的話,則會丟擲NonUniqueResultException異常
3.getResultList
獲取對應的結果集合,指定順序的集合,需要使用List作為返回值型別。如果沒有獲取到資料的話,則返回一個空集合,不會丟擲異常
2.5.分頁 通過setFirstResult() 和setMaxResults() 方法可以完成分頁的查詢
查詢頁碼為0,每頁展示2條資料
List people = entityManager.createQuery("select p from Person p ", Person.class).setFirstResult(0).setMaxResults(2).getResultList();
Tip:不能用於通過集合關係連線的查詢,因為這些查詢可能返回重複的值。
2.6.查詢超時 如果一個應用程式需要設定查詢響應時間的限制,那麼可以在查詢中設定javax.persistence.query.timeout屬性(jpa 2.0引入)或者將它作為持久化屬性的一部分。此屬性定義了查詢在終止前允許允許執行的==毫秒數==。如果查詢超時的時候,會丟擲QueryTimeoutException。
TypedQuery query = entityManager.createQuery("select p from Person p", Person.class);
//單位為毫秒 javax.persistence.query.timeout query.setHint("javax.persistence.query.timeout", 5000); List people = query.getResultList();
2.7.批量更新和刪除 批量更新實體是通過update語句完成。批量刪除實體是通過delete語句完成。兩者皆指定的是實體及其類的屬性。
entityManager.getTransaction().begin(); Query query = entityManager.createQuery("update Person p set p.firstName=:name where p.id=:id"); query.setParameter("name", "xiaobai"); query.setParameter("id", 2); query.executeUpdate();
Query query1 = entityManager.createQuery("delete Person p where p.id=:id"); query1.setParameter("id", 9); query1.executeUpdate(); entityManager.getTransaction().commit();
3.使用JPQL查詢的建議 在應用系統中,通常使用查詢的次數要比增加、修改、刪除要多。故合理的使用查詢顯的尤為重要。
1.建議採用命名查詢(NamedQuery)
持久化提供的程式通常會採用預編譯的方式將命名查詢作為程式初始化階段的一部分。這樣就避免了連續解析JPQL和生成SQL的系統開銷。
2.大數量優先使用投影方式檢索少量的列
jpa查詢通常返回的是整個實體的所有列,但是對於龐大的資料量而言,並不是所有的實體列都需要用到。那麼我們可以使用投影的方式來處理。
List<List<Object[]>> persons = entityManager.createQuery("select new List(firstName,age) from Person p").getResultList(); for (Object o : persons) { System.out.println(o); }
//輸出結果
[Jack, 21]
[Jack, 21]
[Jack, 21]
[lily, 19]
[tom, 23]
[tom, 23]