使用Spring JPA中Page、Pageable接口和Sort類完成分頁排序【專題】


 

首先來說一下分頁和排序所用到的Page、Pageable接口和Sort類都是什么

JpaRepository提供了兩個和分頁和排序有關的查詢

List findAll(Sort sort)                      返回所有實體,按照指定順序排序返回

List findAll(Pageable pageable)   返回實體列表,實體的offest和limit通過pageable來指定

Sort對象用來指示排序,最簡單的Sort對象構造可以傳入一個屬性名列表(不是數據庫列名,是屬性名),默認采用升序排序。例:

Sort sort = new Sort("id");
//或 Sort sort = new Sort(Direction.ASC,"id");
return userDao.findAll(sort);
程序將查詢所有user並按照id進行生序排序。Sort還包括其他一些構造方法,在這里就不一一贅述。

Pageable接口用於構造翻頁查詢,PageRequest是其實現類,可以通過提供的工廠方法創建PageRequest:

public static PageRequest of(int page, int size)
也可以在PageRequest中加入排序:

public static PageRequest of(int page, int size, Sort sort)
方法中的參數,page總是從0開始,表示查詢頁,size指每頁的期望行數

Page接口可以獲得當前頁面的記錄、總頁數、總記錄數、是否有上一頁或下一頁等。Spring Data翻頁查詢總是返回Page對象,Page對象提供了以下常用的方法:

int getTotalPages() 總的頁數
long getTotalElements() 返回總數
List getContent() 返回此次查詢的結果集
————————————————
版權聲明:本文為CSDN博主「來日可期」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/qq_40715775/article/details/83153808

 

JPA分頁
​當請求的數據總量很大時,這時候前端往往都會要求后端將數據分頁返回。本文介紹SpringBoot下后端數據層使用JPA+MySQL時,如何分頁返回數據(除了當前頁面的數據,往往還要返回總頁數這項數據)。

一、從頭到尾自己實現分頁:
Controller層:使用@RequestParam綁定page和pageSize參數,調用Service

Service層:

接收page、pageSize參數,調用Dao獲得當前頁數據;
調用Dao獲得總數據量,再除以pageSize,獲得總頁數;
將當前頁數據和總頁數包裝成VO返回給Controller
DAO層:

自己寫SQL,使用limit語句獲取當前頁數據;
使用 select count(1)獲得數據總量
可以看到,自己實現分頁還是比較麻煩的,不詳細寫具體代碼了,重點介紹下一種方法:

   @Test
    public void whenQuerySeccess() throws Exception{
        mockMvc.perform(
                get("/user")
                        //分頁查詢參數,第四頁,每頁15個數據,按照年齡倒序排序
                        .param("size","15")
                        .param("page","3")//page是從0開始
                        .param("sort","age,desc")//發送get請求,並帶請求參數
 
                .contentType(MediaType.APPLICATION_JSON_UTF8) //編碼格式為json的utf8
        ).andExpect(status().isOk())  //返回的狀態碼為200 OK
                .andExpect(jsonPath("$.length()").value(3))  //判斷返回的json長度是否為3
                .andReturn().getResponse().getContentAsString();
    }

 

二、使用JPA自帶的Pageable和Page:
Controller層:使用Pageable接收參數

    @GetMapping("/byEnterprise")
    public Response<PageVO<QuestionBankVO>> getQuestionBanksByEnterpriseId(@PageableDefault(page = 0, value = 6, sort = {"createdTime"}, direction = Sort.Direction.DESC) Pageable pageable) {
        return ResponseFactory.okResponse(questionBankService.getQuestionBanksByEnterpriseId(pageable, 1));
    }

 

 

Pageable不僅僅支持分頁,還支持排序(單字段/多字段均支持)
@PageableDefault注解可以為pageable對象設置默認參數,即pageable參數非必須傳入,可以利用該注解設置默認值。其中page是頁數(從0開始),value是每頁數據數量(即pageSize),sort是被排序的字段,direction是升序/降序
訪問該RESTful接口時,使用例子:http://localhost:8882/api/v1/questionBanks/byEnterprise?sort=id%2Cdesc&page=0&size=2 (傳direction是在字段后加[,asc/desc],eg:id,desc,代表按id降序排列。也可以只寫id不寫direction,只寫sort字段不寫direction時,direction默認為asc)
Service層:利用DAO層獲得的Page對象,可以獲得當前頁數據、總頁數等信息

    @Override
    public PageVO<QuestionBankVO> getQuestionBanksByEnterpriseId(Pageable pageable, int enterpriseId) {
        Page<QuestionBank> result = questionBankDao.findByEnterpriseIdAndDisabledFalse(pageable, enterpriseId);

        List<QuestionBankVO> bankVOs = result.stream().map(b -> (QuestionBankVO) Converter.map(b, QuestionBankVO.class)).collect(Collectors.toList());
        return new PageVO<>(pageable.getPageNumber(), result.getTotalPages(), bankVOs);
    }

 

DAO層:傳入Pageable,返回Page<T>

public interface QuestionBankDao extends JpaRepository<QuestionBank, Integer> {
    Page<QuestionBank> findByEnterpriseIdAndDisabledFalse(Pageable pageable, int enterpriseId);
}

 

自定義PageVO:

import lombok.Data;

import java.util.List;

/**
 * @author deng
 * @date 2018/12/13
 */
@Data
public class PageVO<T> {
    private int currentPage;
    private int totalPage;
    private List<T> data;

    public PageVO(){
        super();
    }

    public PageVO(int currentPage, int totalPage, List<T> data) {
        this.currentPage = currentPage;
        this.totalPage = totalPage;
        this.data = data;
    }
}

 

 

如果使用Swagger2.8.0【swagger2.7.0中沒有生效】,此時Swagger不會自動顯示出Pageable參數。

想要讓Swagger2能夠針對Pageable參數顯示接口參數,增加如下配置即可:

import com.fasterxml.classmate.TypeResolver;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.data.domain.Pageable;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.AlternateTypeRule;
import springfox.documentation.schema.AlternateTypeRuleConvention;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.util.ArrayList;
import java.util.List;

import static springfox.documentation.schema.AlternateTypeRules.newRule;

@Configuration
@EnableSwagger2
public class SwaggerConfig {

    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select()
                .apis(RequestHandlerSelectors.basePackage("com"))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder().title("通用服務").description("restful 風格接口")
                .version("1.0")
                .contact(new Contact("Coder", "url", "email"))
                .build();
    }

    @Bean
    public AlternateTypeRuleConvention pageableConvention(final TypeResolver resolver) {
        return new AlternateTypeRuleConvention() {
            @Override
            public int getOrder() {
                return Ordered.LOWEST_PRECEDENCE;
            }

            @Override
            public List<AlternateTypeRule> rules() {
                List<AlternateTypeRule> list = new ArrayList<>();
                AlternateTypeRule alternateTypeRule = newRule(resolver.resolve(Pageable.class), resolver.resolve(Page.class));
                list.add(alternateTypeRule);
                return list;
            }
        };
    }

    @ApiModel
    @Data
    static class Page {
        @ApiModelProperty("第page頁,從0開始計數")
        private Integer page;

        @ApiModelProperty("每頁數據數量")
        private Integer size;

        @ApiModelProperty("按屬性排序,格式:屬性(,asc|desc)")
        private List<String> sort;
    }

}

 

配置后Swagger2顯示效果如圖:


Ps: Swagger2的配置學習自:http://blog.51cto.com/7308310/2082742

后話:方法二對JPA框架的依賴性很強(從Controller到Service到DAO都依賴JPA框架);而方法一僅僅在DAO層依賴JPA。比如以后如果因為需求變更而想將框架從JPA遷移至MyBatis的話,方法一只需要修改DAO層代碼,而方法二則從頭到尾全要改。
————————————————
版權聲明:本文為CSDN博主「DengDengLei」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/egg1996911/article/details/85119627

 

 

 

 

顯示時,有三個參數,前兩個必填,第幾頁,一頁多少個size,第三個參數默認可以不填。

但是發現這個方法已經過時了,通過查看它的源碼發現,新方法為靜態方法PageRequest of(page,size)

 

 

 

 

 

 

 分頁是從第0也開始的

Spring項目使用JPA進行數據庫操作可以極大的簡化開發,下面我將用一個完整的Demo為大家展示分頁查詢並顯示在前台頁面
首先來說一下分頁和排序所用到的Page、Pageable接口和Sort類都是什么

JpaRepository提供了兩個和分頁和排序有關的查詢

List findAll(Sort sort)                      返回所有實體,按照指定順序排序返回

List findAll(Pageable pageable)   返回實體列表,實體的offest和limit通過pageable來指定

Sort對象用來指示排序,最簡單的Sort對象構造可以傳入一個屬性名列表(不是數據庫列名,是屬性名),默認采用升序排序。例:

Sort sort = new Sort("id");
//或 Sort sort = new Sort(Direction.ASC,"id");
return userDao.findAll(sort);
程序將查詢所有user並按照id進行生序排序。Sort還包括其他一些構造方法,在這里就不一一贅述。

Pageable接口用於構造翻頁查詢,PageRequest是其實現類,可以通過提供的工廠方法創建PageRequest:

public static PageRequest of(int page, int size)
也可以在PageRequest中加入排序:

public static PageRequest of(int page, int size, Sort sort)
方法中的參數,page總是從0開始,表示查詢頁,size指每頁的期望行數。

Page接口可以獲得當前頁面的記錄、總頁數、總記錄數、是否有上一頁或下一頁等。Spring Data翻頁查詢總是返回Page對象,Page對象提供了以下常用的方法:

int getTotalPages() 總的頁數
long getTotalElements() 返回總數
List getContent() 返回此次查詢的結果集
代碼實現:

1.建立SpringBoot工程,在pom.xml中添加以下依賴

<!--SpringMVC依賴-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--Spring JPA依賴-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- 連接mysql數據庫驅動 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
2.創建實體類

package org.gzc.entity;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class Marker {
@Id
@GeneratedValue
private int id;
private double lng;
private double lat;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public double getLng() {
return lng;
}
public void setLng(double lng) {
this.lng = lng;
}
public double getLat() {
return lat;
}
public void setLat(double lat) {
this.lat = lat;
}
@Override
public String toString() {
return "Marker [id=" + id + ", lng=" + lng + ", lat=" + lat + "]";
}
}
3.編寫dao層接口

package org.gzc.dao;

import org.gzc.entity.Marker;
import org.springframework.data.jpa.repository.JpaRepository;
public interface MarkerDao extends JpaRepository<Marker, Integer>{

}
4.編寫service層接口

package org.gzc.service;

import java.util.List;

import org.gzc.entity.Marker;
import org.springframework.data.domain.Pageable;


public interface MarkerService {
void saveMarker(Marker marker);
Page<Marker> findMarker(Pageable pageable);
}
5.編寫service層實現類

package org.gzc.serviceimpl;

import java.util.List;

import org.gzc.dao.MarkerDao;
import org.gzc.entity.Marker;
import org.gzc.service.MarkerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

@Service
public class MarkerServiceImpl implements MarkerService{

@Autowired
private MarkerDao markerDao;
@Override
public void saveMarker(Marker marker) {
markerDao.save(marker);
}
@Override
public Page<Marker> findMarker(Pageable pageable) {
return markerDao.findAll(pageable);
}

}
6.編寫controller

package org.gzc.controller;

import org.gzc.entity.Marker;
import org.gzc.service.MarkerService;
import org.gzc.util.Result;
import org.gzc.util.ResultUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MapHandlerController {
@Autowired
private MarkerService markerService;
@SuppressWarnings("rawtypes")
@PostMapping("/saveMarker")
public Result saveMarkerController(@RequestBody Marker marker){
System.out.println(marker);
if (marker!=null) {
markerService.saveMarker(marker);
return ResultUtil.success();
}
return ResultUtil.error(1, "保存失敗", "/saveMarker");
}

@SuppressWarnings("rawtypes")
@GetMapping("/showMarkerCount")
public Result returnMarkerCount(){
long count = markerService.markerCount();
System.out.println("count------------------->"+count);
return ResultUtil.success(count, "/showMarkerCount");
}
@SuppressWarnings("rawtypes")
@GetMapping("/showMarkerByPage/{page}")
public Result showMarkerController(@PathVariable("page") int page){
PageRequest pageRequest = PageRequest.of(page, 5);
Page<Marker> markerPage = markerService.findMarker(pageRequest);
for (int i = 0; i < markerPage.getContent().size(); i++) {
System.out.println(markerPage.getContent().get(i));
System.out.println(markerPage.getTotalElements());
}
if (markerPage.getContent()!=null) {
return ResultUtil.success(markerPage.getContent(), "/showMarker");
}else {
return ResultUtil.error(1, "查詢失敗", "/showMarker");
}
}
}
后台先給前台傳過去數據總量,前台計算完顯示第幾頁,再將第幾頁傳送給后台,后台進行查詢並返回數據
---------------------
作者:來日可期
來源:CSDN
原文:https://blog.csdn.net/qq_40715775/article/details/83153808
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!

 

springboot jpa 多條件查詢(多表)

https://www.cnblogs.com/arrrrrya/p/7865090.html

 

2)在StudentService中findByDynamicCases()方法中具體的實現。這里先解釋這幾個對象是什么意思。

Specification:規則、標准。該對象主要是告訴JPA查詢的過濾規則是什么。

Predicate:謂語、斷言。該對象主要是定義具體的判斷條件。如predicate1 = cb.like(sex,"男");即判斷條件為性別為男性。

Root: Root<Student> root就是定義引用root指向Student的包裝對象。Path<String> sex = root.get("sex");即通過root來獲取Student的具體屬性。

CriteriaQuery:查詢條件的組裝。query.where(predicate1,predicate2,predicate3);表示按條件predicate1 and predicate2 and predicate3進行組合條件查詢。

CriteriaBuilder:用來構建CritiaQuery的構建器對象;如:predicate2 = cb.between(age,25,35);表示判斷條件為Student.age between 25 and 25;

我這里的實現是為了演示基於JPA動態查詢(性別為男,年齡在25-25之間,吳國人)我們具體該如何實現。很明顯的看到這段代碼把查詢條件寫死了,不易於擴展。通常情況下是把Specification定義為工具類,每一個判斷條件Predicate定義為Spefication的一個方法,查詢時再將不同的Predicate進行組裝。有興趣的可以自己實現。
---------------------
作者:_artoria_
來源:CSDN
原文:https://blog.csdn.net/UtopiaOfArtoria/article/details/78087494
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!

 

@Query(value = "select * from xxx where if(?1 !='',x1=?1,1=1) and if(?2 !='',x2=?2,1=1)" +
"and if(?3 !='',x3=?3,1=1) ",nativeQuery = true)
List<XXX> find(String X1,String X2,String X3);
工作的時候需求有搜索功能,有三個參數,但是網上找了很多關於jpa多條件查詢的代碼要么在調dao的時候用了大量if判斷,那么需要寫很多查詢方法,要么說的不知所雲,我結合jpa和mysql原語句研究了半天才弄出了這個方法。

xxx是數據庫表名,x1、x2、x3為查詢的字段名。

下面的大寫的XXX是實體類的名,X1X2X3為查詢的參數。

if(?1 !='',x1=?1,1=1) 代表傳入的參數X1如果不為""(Spring類型空是""而不是null)將參數傳入x1,如果為空時顯示1=1 代表參數為真,對查詢結果不產生作用。
---------------------
作者:小寧萌的多肉
來源:CSDN
原文:https://blog.csdn.net/qq_36802726/article/details/81208853
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!

這里,spring data jpa為我們提供了JpaSpecificationExecutor接口,只要簡單實現toPredicate方法就可以實現復雜的查詢


@Repository
public interface MonitorRepository extends JpaRepository<Monitor, Long>, JpaSpecificationExecutor {

}

ctrl類


@GetMapping("/api/listPage")
@ResponseBody
public Map<String, Object> listPage(@RequestParam(value = "pageNumber", defaultValue = "1") Integer pageNumber,
@RequestParam(value = "pageSize", defaultValue = "100") Integer pageSize,
@RequestParam("searchName") String searchName, @RequestParam("searchUrl") String searchUrl) {
//Pageable默認從0開始
pageNumber = pageNumber <= 0 ? 0 : pageNumber - 1;
Pageable pageable = new PageRequest(pageNumber, pageSize);
// Page<Monitor> monitorList = monitorRepository.findMonitorByNameOrUrl(pageable, searchName, urlPath);
Specification specification = new Specification() {
@Override
public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) {
List<Predicate> predicates = new ArrayList<>();
if (StringUtils.isNotBlank(searchName)) {
predicates.add(cb.like(root.get("name"), "%" + searchName + "%"));
}
if (StringUtils.isNotBlank(searchUrl)) {
predicates.add(cb.like(root.get("url"), "%" + searchUrl + "%"));
}
return cb.and(predicates.toArray(new Predicate[predicates.size()]));
}
};
Page<Monitor> monitorList = monitorRepository.findAll(specification, pageable);
Map<String, Object> result = new HashMap<>();
result.put("total", monitorList.getTotalElements());
result.put("rows", monitorList.getContent());
return result;
}


bean類



import com.fasterxml.jackson.annotation.JsonFormat;
import org.hibernate.annotations.CreationTimestamp;

import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;

@Entity
@Table(name = "monitor")
public class Monitor implements Serializable {

private static final long serialVersionUID = 1L;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "m_id", length = 11)
private Long id;

@Column(name = "m_name", length = 64)
private String name;

@Column(name = "m_group", length = 64)
private String group;

@Column(name = "m_url", length = 200)
private String url;

@Temporal(TemporalType.TIMESTAMP)
@Column(name = "create_time")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+08:00")
@CreationTimestamp
private Date createTime;


}

---------------------
作者:elvesfish
來源:CSDN
原文:https://blog.csdn.net/l2000h_ing/article/details/78830769
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!

 

@Query注解的用法(Spring Data JPA)

 

參考文章:http://www.tuicool.com/articles/jQJBNv

 

1. 一個使用@Query注解的簡單例子

@Query(value = "select name,author,price from Book b where b.price>?1 and b.price<?2")
List<Book> findByPriceRange(long price1, long price2);

 

2.  Like表達式

@Query(value = "select name,author,price from Book b where b.name like %:name%")
List<Book> findByNameMatch(@Param("name") String name);

 

3. 使用Native SQL Query

所謂本地查詢,就是使用原生的sql語句(根據數據庫的不同,在sql的語法或結構方面可能有所區別)進行查詢數據庫的操作。

@Query(value = "select * from book b where b.name=?1", nativeQuery = true)
List<Book> findByName(String name);

 

4. 使用@Param注解注入參數

@Query(value = "select name,author,price from Book b where b.name = :name AND b.author=:author AND b.price=:price")
List<Book> findByNamedParam(@Param("name") String name, @Param("author") String author,
        @Param("price") long price);

 

5. SPEL表達式(使用時請參考最后的補充說明)

   '#{#entityName}'值為'Book'對象對應的數據表名稱(book)。

public interface BookQueryRepositoryExample extends Repository<Book, Long>{

       @Query(value = "select * from #{#entityName} b where b.name=?1", nativeQuery = true)
       List<Book> findByName(String name);

}

 

6. 一個較完整的例子

復制代碼
public interface BookQueryRepositoryExample extends Repository<Book, Long> {
    @Query(value = "select * from Book b where b.name=?1", nativeQuery = true) 
    List<Book> findByName(String name);// 此方法sql將會報錯(java.lang.IllegalArgumentException),看出原因了嗎,若沒看出來,請看下一個例子

    @Query(value = "select name,author,price from Book b where b.price>?1 and b.price<?2")
    List<Book> findByPriceRange(long price1, long price2);

    @Query(value = "select name,author,price from Book b where b.name like %:name%")
    List<Book> findByNameMatch(@Param("name") String name);

    @Query(value = "select name,author,price from Book b where b.name = :name AND b.author=:author AND b.price=:price")
    List<Book> findByNamedParam(@Param("name") String name, @Param("author") String author,
            @Param("price") long price);

}
復制代碼

 

7.  解釋例6中錯誤的原因:

     因為指定了nativeQuery = true,即使用原生的sql語句查詢。使用java對象'Book'作為表名來查自然是不對的。只需將Book替換為表名book。

@Query(value = "select * from book b where b.name=?1", nativeQuery = true)
List<Book> findByName(String name);

 

 

補充說明(2017-01-12):

  有同學提出來了,例子5中用'#{#entityName}'為啥取不到值啊?

  先來說一說'#{#entityName}'到底是個啥。從字面來看,'#{#entityName}'不就是實體類的名稱么,對,他就是。

  實體類Book,使用@Entity注解后,spring會將實體類Book納入管理。默認'#{#entityName}'的值就是'Book'。

  但是如果使用了@Entity(name = "book")來注解實體類Book,此時'#{#entityName}'的值就變成了'book'。

  到此,事情就明了了,只需要在用@Entity來注解實體類時指定name為此實體類對應的表名。在原生sql語句中,就可以把'#{#entityName}'來作為數據表名使用。

https://www.cnblogs.com/zj0208/p/6008627.html

 

 

http://docs.jboss.org/hibernate/core/3.3/reference/en/html/queryhql.html

 

JPA 組合查詢之AND和OR組合查詢

    public Page<FinanceBorrow> getUserBorrowByPage(Pageable pageable,JSONObject whereObj,JSONObject userPermission){
        Page<FinanceBorrow> results=financeBorrowRepository.findAll(new Specification<FinanceBorrow>() {
            @Override
            public Predicate toPredicate(Root<FinanceBorrow> root,CriteriaQuery<?>query,CriteriaBuilder cb){
                // 查詢條件
                List<Predicate> listWhere=new ArrayList<>();
                if(whereObj!=null)
                    for (Map.Entry<String, Object> entry : whereObj.entrySet()) {                    
                        try {
                            listWhere.add(cb.like(root.get(entry.getKey()).as(String.class), "%"+entry.getValue()+"%"));                        
                        } catch (IllegalArgumentException e) {
                            log.error("--參數["+entry.getKey()+"]不存在");
                            continue;
                        }                    
                    }
                Predicate[] predicatesWhereArr=new Predicate[listWhere.size()];                
                Predicate predicatesWhere= cb.and(listWhere.toArray(predicatesWhereArr));
                
                //用戶限制條件
                List<Predicate> listPermission=new ArrayList<>();
                if(userPermission!=null)
                    for (Map.Entry<String, Object> entry : userPermission.entrySet()) {                    
                        try {
                            listPermission.add(cb.equal(root.get(entry.getKey()).as(String.class), entry.getValue()));
                            log.info(" -- "+entry.getValue());
                        } catch (IllegalArgumentException e) {
                            log.error("--參數["+entry.getKey()+"]不存在");
                            continue;
                        }                    
                    }
                Predicate[] predicatesPermissionArr=new Predicate[listPermission.size()];                
                Predicate predicatesPermission= cb.or(listPermission.toArray(predicatesPermissionArr));
                
                return query.where(predicatesWhere,predicatesPermission).getRestriction();
            }
            
        },pageable);                    
        return results;
    }

https://blog.csdn.net/langyan122/article/details/80608383

    ExampleMatcher用於創建一個查詢對象,上面的代碼就創建了一個查詢對象。withIgnorePaths方法用來排除某個屬性的查詢。withIncludeNullValues方法讓空值也參與查詢,就是我們設置了對象的姓,而名為空值.

 

1、概念定義:

    上面例子中,是這樣創建“實例”的:Example<Customer> ex = Example.of(customer, matcher);我們看到,Example對象,由customer和matcher共同創建。

    A、實體對象:在持久化框架中與Table對應的域對象,一個對象代表數據庫表中的一條記錄,如上例中Customer對象。在構建查詢條件時,一個實體對象代表的是查詢條件中的“數值”部分。如:要查詢名字是“Dave”的客戶,實體對象只能存儲條件值“Dave”。

    B、匹配器:ExampleMatcher對象,它是匹配“實體對象”的,表示了如何使用“實體對象”中的“值”進行查詢,它代表的是“查詢方式”,解釋了如何去查的問題。如:要查詢FirstName是“Dave”的客戶,即名以“Dave"開頭的客戶,該對象就表示了“以什么開頭的”這個查詢方式,如上例中:withMatcher("name", GenericPropertyMatchers.startsWith())

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

    再來理解“實例查詢”,顧名思義,就是通過一個例子來查詢。要查詢的是Customer對象,查詢條件也是一個Customer對象,通過一個現有的客戶對象作為例子,查詢和這個例子相匹配的對象。

 

2、特點及約束(局限性):

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

    B、不支持過濾條件分組。即不支持過濾條件用 or(或) 來連接,所有的過濾查件,都是簡單一層的用 and(並且) 連接。

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

 

3、ExampleMatcher的使用 :

一些問題:
(1)Null值的處理。當某個條件值為Null,是應當忽略這個過濾條件呢,還是應當去匹配數據庫表中該字段值是Null的記錄?
(2)基本類型的處理。如客戶Customer對象中的年齡age是int型的,當頁面不傳入條件值時,它默認是0,是有值的,那是否參與查詢呢?
(3)忽略某些屬性值。一個實體對象,有許多個屬性,是否每個屬性都參與過濾?是否可以忽略某些屬性?
(4)不同的過濾方式。同樣是作為String值,可能“姓名”希望精確匹配,“地址”希望模糊匹配,如何做到?

(5)大小寫匹配。字符串匹配時,有時可能希望忽略大小寫,有時則不忽略,如何做到?

一些方法:
1、關於基本數據類型。
實體對象中,避免使用基本數據類型,采用包裝器類型。如果已經采用了基本類型,

而這個屬性查詢時不需要進行過濾,則把它添加到忽略列表(ignoredPaths)中。

2、Null值處理方式。

默認值是 IGNORE(忽略),即當條件值為null時,則忽略此過濾條件,一般業務也是采用這種方式就可滿足。當需要查詢數據庫表中屬性為null的記錄時,可將值設為INCLUDE,這時,對於不需要參與查詢的屬性,都必須添加到忽略列表(ignoredPaths)中,否則會出現查不到數據的情況。

3、默認配置、特殊配置。

默認創建匹配器時,字符串采用的是精確匹配、不忽略大小寫,可以通過操作方法改變這種默認匹配,以滿足大多數查詢條件的需要,如將“字符串匹配方式”改為CONTAINING(包含,模糊匹配),這是比較常用的情況。對於個別屬性需要特定的查詢方式,可以通過配置“屬性特定查詢方式”來滿足要求。

4、非字符串屬性

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

5、忽略大小寫的問題。

忽略大小的生效與否,是依賴於數據庫的。例如 MySql 數據庫中,默認創建表結構時,字段是已經忽略大小寫的,所以這個配置與否,都是忽略的。如果業務需要嚴格區分大小寫,可以改變數據庫表結構屬性來實現,具體可百度。

一些例子:
---------------------
作者:緘默的果殼
來源:CSDN
原文:https://blog.csdn.net/qq_30054997/article/details/79420141
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!

 

https://github.com/spring-projects/spring-data-book/tree/master/jpa

 

5.6. Query by Example

5.6.1. Introduction

This chapter provides an introduction to Query by Example and explains how to use it.

Query by Example (QBE) is a user-friendly querying technique with a simple interface. It allows dynamic query creation and does not require you to write queries that contain field names. In fact, Query by Example does not require you to write queries by using store-specific query languages at all.

5.6.2. Usage

The Query by Example API consists of three parts:

  • Probe: The actual example of a domain object with populated fields.

  • ExampleMatcher: The ExampleMatcher carries details on how to match particular fields. It can be reused across multiple Examples.

  • Example: An Example consists of the probe and the ExampleMatcher. It is used to create the query.

Query by Example is well suited for several use cases:

  • Querying your data store with a set of static or dynamic constraints.

  • Frequent refactoring of the domain objects without worrying about breaking existing queries.

  • Working independently from the underlying data store API.

Query by Example also has several limitations:

  • No support for nested or grouped property constraints, such as firstname = ?0 or (firstname = ?1 and lastname = ?2).

  • Only supports starts/contains/ends/regex matching for strings and exact matching for other property types.

Before getting started with Query by Example, you need to have a domain object. To get started, create an interface for your repository, as shown in the following example:

Example 91. Sample Person object
public class Person { @Id private String id; private String firstname; private String lastname; private Address address; // … getters and setters omitted }

The preceding example shows a simple domain object. You can use it to create an Example. By default, fields having nullvalues are ignored, and strings are matched by using the store specific defaults. Examples can be built by either using the offactory method or by using ExampleMatcherExample is immutable. The following listing shows a simple Example:

Example 92. Simple Example
Person person = new Person();  person.setFirstname("Dave");  Example<Person> example = Example.of(person);
  Create a new instance of the domain object.
  Set the properties to query.
  Create the Example.

Examples are ideally be executed with repositories. To do so, let your repository interface extend QueryByExampleExecutor<T>. The following listing shows an excerpt from the QueryByExampleExecutor interface:

Example 93. The  QueryByExampleExecutor
public interface QueryByExampleExecutor<T> { <S extends T> S findOne(Example<S> example); <S extends T> Iterable<S> findAll(Example<S> example); // … more functionality omitted. }

5.6.3. Example Matchers

Examples are not limited to default settings. You can specify your own defaults for string matching, null handling, and property-specific settings by using the ExampleMatcher, as shown in the following example:

Example 94. Example matcher with customized matching
Person person = new Person();  person.setFirstname("Dave");  ExampleMatcher matcher = ExampleMatcher.matching()  .withIgnorePaths("lastname")  .withIncludeNullValues()  .withStringMatcherEnding();  Example<Person> example = Example.of(person, matcher);
  Create a new instance of the domain object.
  Set properties.
  Create an ExampleMatcher to expect all values to match. It is usable at this stage even without further configuration.
  Construct a new ExampleMatcher to ignore the lastname property path.
  Construct a new ExampleMatcher to ignore the lastname property path and to include null values.
  Construct a new ExampleMatcher to ignore the lastname property path, to include null values, and to perform suffix string matching.
  Create a new Example based on the domain object and the configured ExampleMatcher.

By default, the ExampleMatcher expects all values set on the probe to match. If you want to get results matching any of the predicates defined implicitly, use ExampleMatcher.matchingAny().

You can specify behavior for individual properties (such as "firstname" and "lastname" or, for nested properties, "address.city"). You can tune it with matching options and case sensitivity, as shown in the following example:

Example 95. Configuring matcher options
ExampleMatcher matcher = ExampleMatcher.matching() .withMatcher("firstname", endsWith()) .withMatcher("lastname", startsWith().ignoreCase()); }

Another way to configure matcher options is to use lambdas (introduced in Java 8). This approach creates a callback that asks the implementor to modify the matcher. You need not return the matcher, because configuration options are held within the matcher instance. The following example shows a matcher that uses lambdas:

Example 96. Configuring matcher options with lambdas
ExampleMatcher matcher = ExampleMatcher.matching() .withMatcher("firstname", match -> match.endsWith()) .withMatcher("firstname", match -> match.startsWith()); }

Queries created by Example use a merged view of the configuration. Default matching settings can be set at the ExampleMatcher level, while individual settings can be applied to particular property paths. Settings that are set on ExampleMatcher are inherited by property path settings unless they are defined explicitly. Settings on a property patch have higher precedence than default settings. The following table describes the scope of the various ExampleMatcher settings:

Table 4. Scope of  ExampleMatcher settings
Setting Scope

Null-handling

ExampleMatcher

String matching

ExampleMatcher and property path

Ignoring properties

Property path

Case sensitivity

ExampleMatcher and property path

Value transformation

Property path

5.6.4. Executing an example

In Spring Data JPA, you can use Query by Example with Repositories, as shown in the following example:

Example 97. Query by Example using a Repository
public interface PersonRepository extends JpaRepository<Person, String> {  } public class PersonService { @Autowired PersonRepository personRepository; public List<Person> findPeople(Person probe) { return personRepository.findAll(Example.of(probe)); } }
  Currently, only SingularAttribute properties can be used for property matching.

The property specifier accepts property names (such as firstname and lastname). You can navigate by chaining properties together with dots (address.city). You can also tune it with matching options and case sensitivity.

The following table shows the various StringMatcher options that you can use and the result of using them on a field named firstname:

Table 5.  StringMatcher options
Matching Logical result

DEFAULT (case-sensitive)

firstname = ?0

DEFAULT (case-insensitive)

LOWER(firstname) = LOWER(?0)

EXACT (case-sensitive)

firstname = ?0

EXACT (case-insensitive)

LOWER(firstname) = LOWER(?0)

STARTING (case-sensitive)

firstname like ?0 + '%'

STARTING (case-insensitive)

LOWER(firstname) like LOWER(?0) + '%'

ENDING (case-sensitive)

firstname like '%' + ?0

ENDING (case-insensitive)

LOWER(firstname) like '%' + LOWER(?0)

CONTAINING (case-sensitive)

firstname like '%' + ?0 + '%'

CONTAINING (case-insensitive)

LOWER(firstname) like '%' + LOWER(?0) + '%'

 

https://docs.spring.io/spring-data/jpa/docs/2.0.13.RELEASE/reference/html/#query-by-example

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM