zoukankan      html  css  js  c++  java
  • 使用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
    SettingScope

    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
    MatchingLogical 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

  • 相关阅读:
    VS Code C++ 代码格式化方法(clang-format)
    linux函数深入探索——open函数打开文件是否将文件内容加载到内存空间
    总结一下数据库的 一对多、多对一、一对一、多对多 关系
    netstat -st输出解析(二)
    Connection reset by peer的常见原因及解决办法
    Linux errno详解
    Linux之清理linux内存cache
    十大经典排序算法(动图演示)
    android studio导出apk
    多线程还是多进程的选择及区别
  • 原文地址:https://www.cnblogs.com/softidea/p/10376568.html
Copyright © 2011-2022 走看看