【JPA】JPQL查詢 -- 小結
前幾篇部落格對JPA做了巨集觀性質的總結,也搭建成功了JSF + CDI + EJB + JPA(eclipselink)的程式。在通過JPA進行對實體查詢的時候,寫JPQL語句困惑到了我,於是重新總結一下。
Part1: JPQL基本用法
一、Query查詢API
EM提供的建立Query方法:
createNamedQuery(String name)
createNativeQuery(String sqlString)
createNativeQuery(String sqlString, Class resultClass)
createNativeQuery(String sqlString, String resultSetMapping)
createQuery(String jpqlString)
二、引數設定
List getResultList()
Object getSingleResult(); //上述用於select語句
int executeUpdate(); //該句用於update,delete語句
Query setFirstResult(int startPosition)
Query setMaxResult(int maxResult)
三、使用JPQL查詢步驟:
(1)EntityManager建立Query物件
(2)如果包含引數,setParameter()
(3)如果需要分頁,呼叫Query的setFirstResult()或者setMaxResult()
(4)如果是select語句,使用getResultList()或者getSingleResult();
四、執行查詢
P460一個demo就ok,注意返回的結果List,以及兩層for迴圈,最終拿到值,以及內層迴圈中的條件,select的條件個數。
//至此,講解基本用法
Part2: JPQL語法
一、from子句的使用
from後接實體名稱,建議as 別名, from後不會跟多個實體,一般通過隱式連線或者顯式連線來寫實現笛卡爾積或者跨表連線。
二、select子句的使用
select p.name, p from Person as p
(1)select查詢返回結果一般是List集合,如上,返回[String, Person]結果的陣列形式集合(需要兩層迴圈來解析),比如:
for(int i = 0; i < result.size(); i++)
{
Object[] values = (Object[])result.get(i);
System.out.println(values[0] + "-->"
+ values[1]);
}
或者:
for(int i = 0; i < result.size(); i++)
{
Object[] row = (Object[])result.get(i);
for(int j = 0; j < row.length; j++)
{
System.out.println(row[j]);
}
}
(2)特殊情況,select後只有一項(包含實體物件或屬性),則查詢得到的集合元素就是該實體物件或屬性。三、查詢中使用構造器
select new ClassTest(p.name, p.address) from Person as p
如上,返回一個List集合,集合元素是ClassTest.物件(前提:ClassTest是支援以p.name, p.address作為引數的構造器),如果p.name型別為:String; p.address型別為String,則ClassTest有如下構造器ClassTest(String s1, String s2)
四、使用distince排除相同的記錄
select之後跟distinct,在對實體查詢時,通過distinct修飾,就可以去除重複的查詢結果,語句不予展示。
五、where子句和條件表示式
(1)常用運算子:數學、二進位制比較、邏輯、in\not in\ between\ is null \ is empty \ is not empty \ member of \ mot member of 。
(2)like操作符:
select name from Person where name like 'tom%'
上面的運算子的功能大致上和SQL語句中類似的運算子功能相同,可以將SQL中的經驗直接用到JPQL當中。
六、使用JPQL函式
對於JPQL的掌握,一定要向SQL語句那樣, 同時掌握豐富的函式,來幫助我們處理問題。
常用函式:
(1)字串函式
concat(str1,str2)\ substring(str,pos,len)\ trim([leading|trailing|both] sub from str)
(2)數學函式
abs(math_expression)
sqrt(math_express)
mod(number,div)
size(collection_expression)
(3)日期、時間函式
current_date\current_time\current_timestamp
七、多型查詢
例如如下語句:
select p.name from Person p
該查詢不僅會查詢Person全部例項的name屬性,還會將Person的子類例項的name屬性全部查詢出來。
當有繼承關係的實體,查詢父實體的屬性時,會把子實體的name屬性查出來。
八、關聯和連線
參考Part3.
九、使用orderby進行結果排序
select p from Person as p order by p.name, p.age
十、JPQL查詢的聚集函式
JPQL支援在選出的屬性上使用聚集函式,有如下5個:
avg:屬性平均值。
count:統計選擇物件的數量。
max:統計屬性值的最大值。
min:統計屬性值的最小值。
sum:計算屬性值的總和。
十一、使用子查詢
通常用在where子句中過濾查詢結果集,如果底層資料庫支援子查詢,則可以在JPQL語句中使用子查詢,通過英文()括起來。如下:
select fatcat from Cat as fatcat
where fatcat.weight > (select avg(cat.weight) from DomesticCat cat)
如果JPQL子查詢返回多行結果集,則需要使用in、exists、any、all等關鍵字,其中any、all或者some可以和>、<、=等比較運算子結合使用。十二、命名查詢
JPA支援使用Annotation來定義JPQL查詢語句,在Annotation中定義JPQL查詢語句的方式就是命名查詢。JPA提供了@NamedQuery、@NamedQueries兩個Annotation來定義命名查詢。比如,最近做的duke-bookstore專案中就有這樣的使用方法:
@Entity
@Table(name = "WEB_BOOKSTORE_BOOKS")
@NamedQuery(
name = "findBooks",
query = "SELECT b FROM Book b ORDER BY b.bookId")
public class Book implements Serializable {
private static final long serialVersionUID = -4146681491856848089L;
@Id
@NotNull
private String bookId;
private String surname;
private String firstname;
private String title;
private Double price;
private Boolean onsale;
private Integer calendarYear;
private String description;
private Integer inventory;
public Book() {
}
public Book(String bookId, String surname, String firstname, String title,
Double price, Boolean onsale, Integer calendarYear,
String description, Integer inventory) {
this.bookId = bookId;
this.surname = surname;
this.firstname = firstname;
this.title = title;
this.price = price;
this.onsale = onsale;
this.calendarYear = calendarYear;
this.description = description;
this.inventory = inventory;
}
}
在Entity層直接使用@NameQuery定義一個查詢語句,之後在ServiceImpl層的呼叫如下:
public List<Book> getBooks() throws BooksNotFoundException {
try {
return (List<Book>) em.createNamedQuery("findBooks").getResultList();
} catch (Exception ex) {
throw new BooksNotFoundException(
"Could not get books: " + ex.getMessage());
}
}
通過em.createNameQuery("findBooks").getResultList(); 即可實現對@NameQuery的呼叫。十三、批量更新和批量刪除
(1)批量更新:
update <ClassName> set ... [Where where_conditions]
如上所示,即實現了批量更新的寫法格式。
(2)批量刪除:
delete from <ClassName> [where where_conditions]
實現批量刪除的基本語法,和SQL語法很像, 不做過多介紹。Part3:JPQL關於one to one、one to many、many to many
概念:
(1)關係維護端:負責對關係做CRUD操作;負責外來鍵紀錄的更新,關係被維護端沒有權力更新外來鍵紀錄。 (one to many多的一方,many to many則為中間關聯表)
(2)關係被維護端:擁有MappedBy註解,不進行CRUD操作,不維護關係。
(3)分類: one to one、one to many、many to many || 單向、雙向
細緻分析:
請參考我的下一篇部落格關於JPQL實體對映詳細分析。
總結:
針對JPQL,從上面三個角度做了最初的總結,後面遇到問題的解答以及詳細介紹仍在繼續。