Spring Data JPA 實例查詢

1、相關接口方法html

    在繼承JpaRepository接口後,自動擁有了按「實例」進行查詢的諸多方法。這些方法主要在兩個接口中定義,一是QueryByExampleExecutor,一個是JpaRepository,以下所示:
複製代碼
public interface QueryByExampleExecutor<T> { 
<S extends T> S findOne(Example<S> example); //根據「實例」查找一個對象。
<S extends T> Iterable<S> findAll(Example<S> example); //根據「實例」查找一批對象
<S extends T> Iterable<S> findAll(Example<S> example, Sort sort); //根據「實例」查找一批對象,且排序
<S extends T> Page<S> findAll(Example<S> example, Pageable pageable); //根據「實例」查找一批對象,且排序和分頁
<S extends T> long count(Example<S> example); //根據「實例」查找,返回符合條件的對象個數
<S extends T> boolean exists(Example<S> example); //根據「實例」判斷是否有符合條件的對象
}
複製代碼

 

複製代碼
@NoRepositoryBean public interface JpaRepository<T, ID extends Serializable> 
  extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
 ...... 
@Override 
<S extends T> List<S> findAll(Example<S> example); //根據實例查詢 
@Override 
<S extends T> List<S> findAll(Example<S> example, Sort sort);//根據實例查詢,並排序。 
}
複製代碼

 

2、快速入門正則表達式

    觀察上面的方法,其參數都有一個 Example 類對象,這就一個「實例」(我的翻譯的叫法,未必準確),它表明的是查詢條件,它是經過包裝要查詢的實體對象而生成的。下面經過一個例子,來快速瞭解一下其用法。
 
一、定義實體類
 若有 客戶信息、客戶類型 兩個實體類定義以下:
複製代碼
/**
* 客戶
*/
@Entity
@Table(name = "demo_lx_Customer")
public class Customer extends BaseBo
{
  private String name; //姓名
  private String sex; //性別
  private int age; //年齡
  private String address; //地址
  private boolean focus ; //是否重點關注
  private Date addTime; //建立時間
  private String remark; //備註
  @ManyToOne
  private CustomerType customerType; //客戶類型
  ......
}
複製代碼

 

 
複製代碼
/**
* 客戶類型
*/
@Entity
@Table(name = "demo_lx_CustomerType")
public class CustomerType extends BaseBo
{ 
  private String code; //編號
  private String name; //名稱
  private String remark; //備註
  ......
}
複製代碼

 

二、模擬數據數據庫

加入一些模擬數據:

 

三、查詢編程

如今要查詢:地址是「鄭州市」,姓「劉」的客戶,能夠這樣來寫:框架

複製代碼
     //建立查詢條件數據對象
        Customer customer = new Customer();
        customer.setName("劉");
        customer.setAddress("河南省鄭州市");

        //建立匹配器,即如何使用查詢條件
        ExampleMatcher matcher = ExampleMatcher.matching() //構建對象
                .withMatcher("name", GenericPropertyMatchers.startsWith()) //姓名採用「開始匹配」的方式查詢
                .withIgnorePaths("focus");  //忽略屬性:是否關注。由於是基本類型,須要忽略掉
        
        //建立實例
        Example<Customer> ex = Example.of(customer, matcher); 
        
        //查詢
        List<Customer> ls = dao.findAll(ex);
        
        //輸出結果
        System.out.println("數量:"+ls.size());
        for (Customer bo:ls)
        {
            System.out.println(bo.getName());
        }
複製代碼

 

輸出結果:
數量:1
劉芳

 

3、認識「實例查詢」ide

 

一、概念定義:工具

    上面例子中,是這樣建立「實例」的:Example<Customer> ex = Example.of(customer, matcher);咱們看到,Example對象,由customer和matcher共同建立,爲講解方便,咱們先來明確一些定義。spa

    A、實體對象:在持久化框架中與Table對應的域對象,一個對象表明數據庫表中的一條記錄,如上例中Customer對象。在構建查詢條件時,一個實體對象表明的是查詢條件中的「數值」部分。如:要查詢姓「劉」的客戶,實體對象只能存儲條件值「劉」。翻譯

    B、匹配器:ExampleMatcher對象,它是匹配「實體對象」的,表示瞭如何使用「實體對象」中的「值」進行查詢,它表明的是「查詢方式」,解釋瞭如何去查的問題。如:要查詢姓「劉」的客戶,即姓名以「劉」開頭的客戶,該對象就表示了「以某某開頭的」這個查詢方式,如上例中:withMatcher("name", GenericPropertyMatchers.startsWith())code

    C、實例:即Example對象,表明的是完整的查詢條件。由實體對象(查詢條件值)和匹配器(查詢方式)共同建立。

    再來理解「實例查詢」,顧名思義,就是經過一個例子來查詢。要查詢的是Customer對象,查詢條件也是一個Customer對象,經過一個現有的客戶對象做爲例子,查詢和這個例子相匹配的對象。

 

二、特色及約束(侷限性):

    一、支持動態查詢。即支持查詢條件個數不固定的狀況,如:客戶列表中有多個過濾條件,用戶使用時在「地址」查詢框中輸入了值,就須要按地址進行過濾,若是沒有輸入值,就忽略這個過濾條件。對應的實現是,在構建查詢條件Customer對象時,將address屬性值置具體的條件值或置爲null。

    二、不支持過濾條件分組。即不支持過濾條件用 or(或) 來鏈接,全部的過濾查件,都是簡單一層的用 and(而且) 鏈接。

    三、僅支持字符串的開始/包含/結束/正則表達式匹配 和 其餘屬性類型的精確匹配。查詢時,對一個要進行匹配的屬性(如:姓名 name),只能傳入一個過濾條件值,如以Customer爲例,要查詢姓「劉」的客戶,「劉」這個條件值就存儲在表示條件對象的Customer對象的name屬性中,針對於「姓名」的過濾也只有這麼一個存儲過濾值的位置,沒辦法同時傳入兩個過濾值。正是因爲這個限制,有些查詢是沒辦法支持的,例如要查詢某個時間段內添加的客戶,對應的屬性是 addTime,須要傳入「開始時間」和「結束時間」兩個條件值,而這種查詢方式沒有存兩個值的位置,因此就沒辦法完成這樣的查詢。

 

4、重點理解ExampleMatcher

 

一、須要考慮的因素

    查詢條件的表示,有兩部分,一是條件值,二是查詢方式。條件值用實體對象(如Customer對象)來存儲,相對簡單,當頁面傳入過濾條件值時,存入相對應的屬性中,沒入傳入時,屬性保持默認值。查詢方式是用匹配器ExampleMatcher來表示,狀況相對複雜些,須要考慮的因素有:
(1)Null值的處理。當某個條件值爲Null,是應當忽略這個過濾條件呢,仍是應當去匹配數據庫表中該字段值是Null的記錄?
(2)基本類型的處理。如客戶Customer對象中的年齡age是int型的,當頁面不傳入條件值時,它默認是0,是有值的,那是否參與查詢呢?
(3)忽略某些屬性值。一個實體對象,有許多個屬性,是否每一個屬性都參與過濾?是否能夠忽略某些屬性?
(4)不一樣的過濾方式。一樣是做爲String值,可能「姓名」但願精確匹配,「地址」但願模糊匹配,如何作到?
(5)大小寫匹配。字符串匹配時,有時可能但願忽略大小寫,有時則不忽略,如何作到?
 
二、五個配置項
    圍繞上面一系列狀況,ExampleMatcher中定義了5項配置來解決這些問題。 
複製代碼
public class ExampleMatcher {
NullHandler nullHandler; //Null值處理方式
StringMatcher defaultStringMatcher; //默認字符串匹配方式
boolean defaultIgnoreCase; //默認大小寫忽略方式
PropertySpecifiers propertySpecifiers; //各屬性特定查詢方式
Set<String> ignoredPaths; //忽略屬性列表
......
}
複製代碼

    (1)nullHandler:Null值處理方式,枚舉類型,有2個可選值,INCLUDE(包括),IGNORE(忽略)。標識做爲條件的實體對象中,一個屬性值(條件值)爲Null是,是否參與過濾。當該選項值是INCLUDE時,表示仍參與過濾,會匹配數據庫表中該字段值是Null的記錄;若爲IGNORE值,表示不參與過濾。

    (2) defaultStringMatcher:默認字符串匹配方式,枚舉類型,有6個可選值,DEFAULT(默認,效果同EXACT),EXACT(相等),STARTING(開始匹配),ENDING(結束匹配),CONTAINING(包含,模糊匹配),REGEX(正則表達式)。該配置對全部字符串屬性過濾有效,除非該屬性在 propertySpecifiers 中單獨定義本身的匹配方式。
    (3) defaultIgnoreCase:默認大小寫忽略方式,布爾型,當值爲false時,即不忽略,大小不相等。該配置對全部字符串屬性過濾有效,除非該屬性在 propertySpecifiers 中單獨定義本身的忽略大小寫方式。
    (4) propertySpecifiers:各屬性特定查詢方式,描述了各個屬性單獨定義的查詢方式,每一個查詢方式中包含4個元素:屬性名、字符串匹配方式、大小寫忽略方式、屬性轉換器。若是屬性未單獨定義查詢方式,或單獨查詢方式中,某個元素未定義(如:字符串匹配方式),則採用 ExampleMatcher 中定義的默認值,即上面介紹的 defaultStringMatcher 和 defaultIgnoreCase 的值。
    (5) ignoredPaths:忽略屬性列表,忽略的屬性不參與查詢過濾。
 
 
 
三、操做方法

在ExampleMatcher中定義了一系列方式,用於設置這5項設置值,全部的設置方法均返回 ExampleMatcher 對象,因此支持鏈式編程配置。


(1)建立一個默認的 ExampleMatcher 對象。

定義:

public static ExampleMatcher matching()

默認配置以下:
A、nullHandler:IGNORE。Null值處理方式:忽略
B、defaultStringMatcher:DEFAULT。默認字符串匹配方式:默認(相等
C、defaultIgnoreCase:false。默認大小寫忽略方式:不忽略
D、propertySpecifiers:空。各屬性特定查詢方式,
E、ignoredPaths:空列表。忽略屬性列表,空列表

 

(2)改變Null值處理方式。

定義:
public ExampleMatcher withNullHandler(NullHandler nullHandler)
public ExampleMatcher withIncludeNullValues()
public ExampleMatcher withIgnoreNullValues()

產生效果:
改變配置項nullHandler,分別設爲:指定值、INCLUDE(包括)、IGNORE(忽略)。

 

(3)改變默認字符串匹配方式。
定義:
public ExampleMatcher withStringMatcher(StringMatcher defaultStringMatcher)

產生效果:
改變配置項defaultStringMatcher,設爲指定值。

 

(4)改變默認大小寫忽略方式。
定義:
public ExampleMatcher withIgnoreCase()
public ExampleMatcher withIgnoreCase(boolean defaultIgnoreCase)

產生效果:
改變配置項defaultIgnoreCase,分別設爲:true,指定值。

 

(5)向「忽略屬性列表」中添加屬性。
定義:
public ExampleMatcher withIgnorePaths(String... ignoredPaths)

產生效果:
改變配置項ignoredPaths,向列表中添加一個或多個屬性。

 

(6)配置屬性特定查詢方式

    一個屬性的特定查詢方式,包含了3個信息:字符串匹配方式、大小寫忽略方式、屬性轉換器,存儲在 propertySpecifiers 中,操做時用 GenericPropertyMatcher 類來傳遞配置信息。有4個方法來改變配置,這4個方法操做時,內部均採用增量改變的方式,即若是沒有爲屬性定義「特定查詢方式」,則會定義一個,並根據傳進來的「非空信息」進行配置,若是已經定義有,則會根據傳進來的「非空信息」進行更新。若是一個「特定查詢方式」中的「字符串匹配方式、大小寫忽略方式」沒有設置值,查詢時則採用ExampleMatcher中的默認配置。

 

A、自定義類的方式。

定義:
public ExampleMatcher withMatcher(String propertyPath, MatcherConfigurer<GenericPropertyMatcher> matcherConfigurer)

產生效果:

向 propertySpecifiers 中增長或更新屬性「特定查詢方式」的配置。

參數說明:
propertyPath:要配置特定查詢的屬性名。
matcherConfigurer:自定義類對象。自定義類須要實現MatcherConfigurer接口,在接口的 configureMatcher() 實現方法中指定相關配置。

 

B、直接傳入通用屬性查詢對象方式。

定義:
public ExampleMatcher withMatcher(String propertyPath, GenericPropertyMatcher genericPropertyMatcher)

產生效果:

向 propertySpecifiers 中增長或更新屬性「特定查詢方式」的配置。

參數說明:
propertyPath:要配置特定查詢的屬性名。
genericPropertyMatcher:直接傳入一個通用查詢對象。 ExampleMatcher.GenericPropertyMatchers工具類中提供了經常使用對象建立的靜態方法,全部方法均返回 GenericPropertyMatcher 對象,因此支持鏈式編程配置。

另外:GenericPropertyMatcher 類自己也提供了諸多方法,用於改變相關配置項。

 

C、改變的大小寫忽略方式

定義:
public ExampleMatcher withIgnoreCase(String... propertyPaths)

產生效果:
向 propertySpecifiers 中增長或更新屬性「特定查詢方式」中的「大小寫忽略方式」配置。。

 

D、設置屬性轉換器

定義:
public ExampleMatcher withTransformer(String propertyPath, PropertyValueTransformer propertyValueTransformer)

產生效果:
向 propertySpecifiers 中增長或更新屬性「特定查詢方式」中的「屬性轉換器」配置。
 

5、經常使用狀況說明

  

一、關於基本數據類型。
實體對象中,避免使用基本數據類型,採用包裝器類型。若是已經採用了基本類型,
而這個屬性查詢時不須要進行過濾,則把它添加到忽略列表(ignoredPaths)中。

二、Null值處理方式。
默認值是 IGNORE(忽略),即當條件值爲null時,則忽略此過濾條件,通常業務也是採用這種方式就可知足。當須要查詢數據庫表中屬性爲null的記錄時,可將值設爲INCLUDE,這時,對於不須要參與查詢的屬性,都必須添加到忽略列表(ignoredPaths)中,不然會出現查不到數據的狀況。

三、默認配置、特殊配置。
默認建立匹配器時,字符串採用的是精確匹配、不忽略大小寫,能夠經過操做方法改變這種默認匹配,以知足大多數查詢條件的須要,如將「字符串匹配方式」改成CONTAINING(包含,模糊匹配),這是比較經常使用的狀況。對於個別屬性須要特定的查詢方式,能夠經過配置「屬性特定查詢方式」來知足要求。

四、非字符串屬性
如約束中所談,非字符串屬性均採用精確匹配,即等於。

五、忽略大小寫的問題。
忽略大小的生效與否,是依賴於數據庫的。例如 MySql 數據庫中,默認建立表結構時,字段是已經忽略大小寫的,因此這個配置與否,都是忽略的。若是業務須要嚴格區分大小寫,能夠改變數據庫表結構屬性來實現,具體可百度。

  

6、經常使用查詢示例

以「快速入門」中的實體對象和模擬數據爲例,列一些經常使用查詢,方便開發時參考。

 

一、無匹配器的狀況
要求:查詢地址是「河南省鄭州市」,且重點關注的客戶。
說明:對於默認匹配器知足條件時,則不須要建立匹配器。

複製代碼
     //建立查詢條件數據對象
        Customer customer = new Customer();
        customer.setAddress("河南省鄭州市");
        customer.setFocus(true);
        
        //建立實例
        Example<Customer> ex = Example.of(customer); 
        
        //查詢
        List<Customer> ls = dao.findAll(ex);
        
        //輸出結果
        System.out.println("數量:"+ls.size());
        for (Customer bo:ls)
        {
            System.out.println(bo.getName());
        }
複製代碼

 

輸出結果:

數量:4
李明
劉芳
zhang ming
ZHANG SAN

 

二、通用狀況
要求:根據姓名、地址、備註進行模糊查詢,忽略大小寫,地址要求開始匹配。
說明:這是通用狀況,主要演示改變默認字符串匹配方式、改變默認大小寫忽略方式、屬性特定查詢方式配置、忽略屬性列表配置。

複製代碼
     //建立查詢條件數據對象
        Customer customer = new Customer();
        customer.setName("zhang");
        customer.setAddress("河南省");
        customer.setRemark("BB");

        //建立匹配器,即如何使用查詢條件
        ExampleMatcher matcher = ExampleMatcher.matching() //構建對象
                .withStringMatcher(StringMatcher.CONTAINING) //改變默認字符串匹配方式:模糊查詢
                .withIgnoreCase(true) //改變默認大小寫忽略方式:忽略大小寫
                .withMatcher("address", GenericPropertyMatchers.startsWith()) //地址採用「開始匹配」的方式查詢
                .withIgnorePaths("focus");  //忽略屬性:是否關注。由於是基本類型,須要忽略掉
        
        //建立實例
        Example<Customer> ex = Example.of(customer, matcher); 
        
        //查詢
        List<Customer> ls = dao.findAll(ex);
        
        //輸出結果
        System.out.println("數量:"+ls.size());
        for (Customer bo:ls)
        {
            System.out.println(bo.getName());
        }
複製代碼

 

輸出結果: 

數量:2
zhang ming
ZHANG SAN

  

三、多級查詢
要求:查詢全部潛在客戶
說明:主要演示多層級屬性查詢

複製代碼
     //建立查詢條件數據對象
        CustomerType type = new CustomerType();
        type.setCode("01"); //編號01表明潛在客戶
        Customer customer = new Customer();
        customer.setCustomerType(type);        

        //建立匹配器,即如何使用查詢條件
        ExampleMatcher matcher = ExampleMatcher.matching() //構建對象
                .withIgnorePaths("focus");  //忽略屬性:是否關注。由於是基本類型,須要忽略掉                
        
        //建立實例
        Example<Customer> ex = Example.of(customer, matcher); 
        
        //查詢
        List<Customer> ls = dao.findAll(ex);
        
        //輸出結果
        System.out.println("數量:"+ls.size());
        for (Customer bo:ls)
        {
            System.out.println(bo.getName());
        }
複製代碼

 

輸出結果: 

數量:4
李明
李莉
張強
ZHANG SAN

  

四、查詢Null值
要求:地址是null的客戶
說明:主要演示改變「Null值處理方式」

複製代碼
     //建立查詢條件數據對象
        Customer customer = new Customer();

        //建立匹配器,即如何使用查詢條件
        ExampleMatcher matcher = ExampleMatcher.matching() //構建對象
                .withIncludeNullValues() //改變「Null值處理方式」:包括
                .withIgnorePaths("id","name","sex","age","focus","addTime","remark","customerType");  //忽略其餘屬性
        
        //建立實例
        Example<Customer> ex = Example.of(customer, matcher); 
        
        //查詢
        List<Customer> ls = dao.findAll(ex);
        
        //輸出結果
        System.out.println("數量:"+ls.size());
        for (Customer bo:ls)
        {
            System.out.println(bo.getName());
        }
複製代碼

 

輸出結果: 

數量:2
張強
劉明

 

7、寫在最後

對實例查詢的研究及本文的寫做,確入投入了精力,所以該文章也贏得了很多的點擊率。
可是...可是...就實用性而言,因爲「實例查詢」的侷限性和複雜性,在Spring Data JPA中實際上是不實用的。用的最多的仍是「簡單查詢」(由於簡單,因此...)和 基於JPA Criteria 的動態查詢(能夠知足全部需求,沒有侷限性)。(僅我的觀點)
簡單查詢的兩篇博文:
相關文章
相關標籤/搜索