1. 程式人生 > >【JPA】JPQL查詢 -- 小結

【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,從上面三個角度做了最初的總結,後面遇到問題的解答以及詳細介紹仍在繼續。