一、相關接口方法
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);//根據實例查詢,並排序。 }
二、快速入門
/** * 客戶 */ @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; //備注 ...... }
2、模擬數據
3、查詢
現在要查詢:地址是“鄭州市”,姓“劉”的客戶,可以這樣來寫:
//創建查詢條件數據對象 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 劉芳
三、認識“實例查詢”
1、概念定義:
上面例子中,是這樣創建“實例”的:Example<Customer> ex = Example.of(customer, matcher);我們看到,Example對象,由customer和matcher共同創建,為講解方便,我們先來明確一些定義。
A、實體對象:在持久化框架中與Table對應的域對象,一個對象代表數據庫表中的一條記錄,如上例中Customer對象。在構建查詢條件時,一個實體對象代表的是查詢條件中的“數值”部分。如:要查詢姓“劉”的客戶,實體對象只能存儲條件值“劉”。
B、匹配器:ExampleMatcher對象,它是匹配“實體對象”的,表示了如何使用“實體對象”中的“值”進行查詢,它代表的是“查詢方式”,解釋了如何去查的問題。如:要查詢姓“劉”的客戶,即姓名以“劉”開頭的客戶,該對象就表示了“以某某開頭的”這個查詢方式,如上例中:withMatcher("name", GenericPropertyMatchers.startsWith())
C、實例:即Example對象,代表的是完整的查詢條件。由實體對象(查詢條件值)和匹配器(查詢方式)共同創建。
再來理解“實例查詢”,顧名思義,就是通過一個例子來查詢。要查詢的是Customer對象,查詢條件也是一個Customer對象,通過一個現有的客戶對象作為例子,查詢和這個例子相匹配的對象。
2、特點及約束(局限性):
1、支持動態查詢。即支持查詢條件個數不固定的情況,如:客戶列表中有多個過濾條件,用戶使用時在“地址”查詢框中輸入了值,就需要按地址進行過濾,如果沒有輸入值,就忽略這個過濾條件。對應的實現是,在構建查詢條件Customer對象時,將address屬性值置具體的條件值或置為null。
2、不支持過濾條件分組。即不支持過濾條件用 or(或) 來連接,所有的過濾查件,都是簡單一層的用 and(並且) 連接。
3、僅支持字符串的開始/包含/結束/正則表達式匹配 和 其他屬性類型的精確匹配。查詢時,對一個要進行匹配的屬性(如:姓名 name),只能傳入一個過濾條件值,如以Customer為例,要查詢姓“劉”的客戶,“劉”這個條件值就存儲在表示條件對象的Customer對象的name屬性中,針對於“姓名”的過濾也只有這么一個存儲過濾值的位置,沒辦法同時傳入兩個過濾值。正是由於這個限制,有些查詢是沒辦法支持的,例如要查詢某個時間段內添加的客戶,對應的屬性是 addTime,需要傳入“開始時間”和“結束時間”兩個條件值,而這種查詢方式沒有存兩個值的位置,所以就沒辦法完成這樣的查詢。
四、重點理解ExampleMatcher
1、需要考慮的因素
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值,表示不參與過濾。

在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 中增加或更新屬性“特定查詢方式”中的“屬性轉換器”配置。
五、常用情況說明
1、關於基本數據類型。
實體對象中,避免使用基本數據類型,采用包裝器類型。如果已經采用了基本類型,
而這個屬性查詢時不需要進行過濾,則把它添加到忽略列表(ignoredPaths)中。
2、Null值處理方式。
默認值是 IGNORE(忽略),即當條件值為null時,則忽略此過濾條件,一般業務也是采用這種方式就可滿足。當需要查詢數據庫表中屬性為null的記錄時,可將值設為INCLUDE,這時,對於不需要參與查詢的屬性,都必須添加到忽略列表(ignoredPaths)中,否則會出現查不到數據的情況。
3、默認配置、特殊配置。
默認創建匹配器時,字符串采用的是精確匹配、不忽略大小寫,可以通過操作方法改變這種默認匹配,以滿足大多數查詢條件的需要,如將“字符串匹配方式”改為CONTAINING(包含,模糊匹配),這是比較常用的情況。對於個別屬性需要特定的查詢方式,可以通過配置“屬性特定查詢方式”來滿足要求。
4、非字符串屬性
如約束中所談,非字符串屬性均采用精確匹配,即等於。
5、忽略大小寫的問題。
忽略大小的生效與否,是依賴於數據庫的。例如 MySql 數據庫中,默認創建表結構時,字段是已經忽略大小寫的,所以這個配置與否,都是忽略的。如果業務需要嚴格區分大小寫,可以改變數據庫表結構屬性來實現,具體可百度。
六、常用查詢示例
以“快速入門”中的實體對象和模擬數據為例,列一些常用查詢,方便開發時參考。
1、無匹配器的情況
要求:查詢地址是“河南省鄭州市”,且重點關注的客戶。
說明:對於默認匹配器滿足條件時,則不需要創建匹配器。
//創建查詢條件數據對象 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
2、通用情況
要求:根據姓名、地址、備注進行模糊查詢,忽略大小寫,地址要求開始匹配。
說明:這是通用情況,主要演示改變默認字符串匹配方式、改變默認大小寫忽略方式、屬性特定查詢方式配置、忽略屬性列表配置。
//創建查詢條件數據對象 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
3、多級查詢
要求:查詢所有潛在客戶
說明:主要演示多層級屬性查詢
//創建查詢條件數據對象 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
4、查詢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 張強 劉明
七、寫在最后