Spring Data JPA实现动态条件与范围查询实例代码

Spring Data JPA为我们提供了Query With Example来实现动态条件查询,当查询条件为空的时候,我们不用做大量的条件判断。但是Query With Example却不支持范围查询(包括日期范围,数值范围查询),本文通过Specification实现了既支持动态条件查询又支持范围查询的方法。

1 实现方式

1.1 范围对象Range定义

import java.io.Serializable;

public class Range<E> implements Serializable {
  private static final long serialVersionUID = 1L;

  private String field;
  private Comparable from;
  private Comparable to;
  private Boolean includeNull;

  public Range(String field) {
    this.field = field;
  }

  public Range(String field, Comparable from, Comparable to) {
    this.field = field;
    this.from = from;
    this.to = to;
  }

  public Range(String field, Comparable from, Comparable to, Boolean includeNull) {
    this.field = field;
    this.from = from;
    this.to = to;
    this.includeNull = includeNull;
  }

  public Range(Range<E> other) {
    this.field = other.getField();
    this.from = other.getFrom();
    this.to = other.getTo();
    this.includeNull = other.getIncludeNull();
  }

  public String getField() {
    return field;
  }

  public Comparable getFrom() {
    return from;
  }

  public void setFrom(Comparable from) {
    this.from = from;
  }

  public boolean isFromSet() {
    return getFrom() != null;
  }

  public Comparable getTo() {
    return to;
  }

  public void setTo(Comparable to) {
    this.to = to;
  }

  public boolean isToSet() {
    return getTo() != null;
  }

  public void setIncludeNull(boolean includeNull) {
    this.includeNull = includeNull;
  }

  public Boolean getIncludeNull() {
    return includeNull;
  }

  public boolean isIncludeNullSet() {
    return includeNull != null;
  }

  public boolean isBetween() {
    return isFromSet() && isToSet();
  }

  public boolean isSet() {
    return isFromSet() || isToSet() || isIncludeNullSet();
  }

  public boolean isValid() {
    if (isBetween()) {
      return getFrom().compareTo(getTo()) <= 0;
    }

    return true;
  }
}

1.2 example的Specification

import org.springframework.data.domain.Example;
import org.springframework.data.jpa.convert.QueryByExamplePredicateBuilder;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.util.Assert;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

/**
 * Created by wangyunfei on 2017/6/6.
 */
public class ByExampleSpecification<T> implements Specification<T> {
  private final Example<T> example;

  public ByExampleSpecification(Example<T> example) {

    Assert.notNull(example, "Example must not be null!");
    this.example = example;
  }

  @Override
  public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
    return QueryByExamplePredicateBuilder.getPredicate(root, cb, example);
  }
}

1.3 Range的Specification

import org.springframework.data.jpa.domain.Specification;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.List;

import static com.google.common.collect.Iterables.toArray;
import static com.google.common.collect.Lists.newArrayList;
import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;

/**
 * Created by wangyunfei on 2017/6/6.
 */
public class ByRangeSpecification<T> implements Specification<T> {
  private final List<Range<T>> ranges;

  public ByRangeSpecification(List<Range<T>> ranges) {
    this.ranges = ranges;
  }

  @Override
  public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
    List<Predicate> predicates = newArrayList();

    for (Range<T> range : ranges) {
      if (range.isSet()) {
        Predicate rangePredicate = buildRangePredicate(range, root, builder);

        if (rangePredicate != null) {
          if (!range.isIncludeNullSet() || range.getIncludeNull() == FALSE) {
            predicates.add(rangePredicate);
          } else {
            predicates.add(builder.or(rangePredicate, builder.isNull(root.get(range.getField()))));
          }
        }

        if (TRUE == range.getIncludeNull()) {
          predicates.add(builder.isNull(root.get(range.getField())));
        } else if (FALSE == range.getIncludeNull()) {
          predicates.add(builder.isNotNull(root.get(range.getField())));
        }
      }
    }
    return predicates.isEmpty() ? builder.conjunction() : builder.and(toArray(predicates, Predicate.class));
  }

  private Predicate buildRangePredicate(Range<T> range, Root<T> root, CriteriaBuilder builder) {
    if (range.isBetween()) {
      return builder.between(root.get(range.getField()), range.getFrom(), range.getTo());
    } else if (range.isFromSet()) {
      return builder.greaterThanOrEqualTo(root.get(range.getField()), range.getFrom());
    } else if (range.isToSet()) {
      return builder.lessThanOrEqualTo(root.get(range.getField()), range.getTo());
    }
    return null;
  }

}

1.4 自定义Repository与实现

import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.NoRepositoryBean;

import java.io.Serializable;
import java.util.List;

@NoRepositoryBean
public interface WiselyRepository<E, PK extends Serializable> extends JpaRepository<E, PK> {

 Page<E> queryByExampleWithRange(Example example,List<Range<E>> ranges, Pageable pageable);

}
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;

import javax.persistence.EntityManager;
import java.io.Serializable;
import java.util.List;

import static org.springframework.data.jpa.domain.Specifications.where;

public class WiselyRepositoryImpl<E, PK extends Serializable> extends SimpleJpaRepository<E, PK> implements
    WiselyRepository<E, PK> {
  private final EntityManager entityManager;

  public WiselyRepositoryImpl(JpaEntityInformation entityInformation, EntityManager entityManager) {
    super(entityInformation, entityManager);
    this.entityManager = entityManager;
  }

  @Override
  public Page<E> queryByExampleWithRange(Example example, List<Range<E>> ranges, Pageable pageable) {
    Specification<E> byExample = new ByExampleSpecification<>(example);
    Specification<E> byRanges = new ByRangeSpecification<>(ranges);
    return findAll(where(byExample).and(byRanges),pageable);
  }
}

2 使用方式

2.1 开启支持

通过@EnableJpaRepositories(repositoryBaseClass = WiselyRepositoryImpl.class)开启对定义功能的支持。

2.2 示例

实体类

@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
  @Id
  @GeneratedValue
  private Long id;
  private String name;
  private Integer height;
  @DateTimeFormat(pattern = "yyyy-MM-dd")
  private Date birthday;
}

PersonRepository

public interface PersonRepository extends WiselyRepository<Person,Long> {
}

测试控制器

@RestController
@RequestMapping("/people")
public class PersonController {

  @Autowired
  PersonRepository personRepository;

  @PostMapping
  public ResponseEntity<Person> save(@RequestBody Person person){
    Person p = personRepository.save(person);
    return new ResponseEntity<Person>(p, HttpStatus.CREATED);
  }

  @GetMapping
  public ResponseEntity<Page<Person>> query(Person person,
                       @DateTimeFormat(pattern = "yyyy-MM-dd")Date startDate,
                       @DateTimeFormat(pattern = "yyyy-MM-dd")Date endDate,
                       Integer startHeight,
                       Integer endHeight,
                       Pageable pageable){
    Example<Person> personExample = Example.of(person);

    List<Range<Person>> ranges = newArrayList();
    Range<Person> birthRange = new Range<Person>("birthday",startDate,endDate);
    Range<Person> heightRange = new Range<Person>("height",startHeight,endHeight);
    ranges.add(birthRange);
    ranges.add(heightRange);

    Page<Person> page = personRepository.queryByExampleWithRange(personExample,ranges,pageable);

    return new ResponseEntity<Page<Person>>(page,HttpStatus.OK);

  }
}

源码地址:https://github.com/wiselyman/query_with_example_and_range

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Spring Data JPA中的动态查询实例

    spring Data JPA大大的简化了我们持久层的开发,但是实际应用中,我们还是需要动态查询的. 比如,前端有多个条件,这些条件很多都是可选的,那么后端的SQL,就应该是可以定制的,在使用hibernate的时候,可以通过判断条件来拼接SQL(HQL),当然,Spring Data JPA在简化我们开发的同时,也是提供了支持的. 通过实现Criteria二实现的动态查询,需要我们的Repo接口继承JpaSpecificationExecutor接口,这是个泛型接口. 然后查询的时候,传入动态

  • Spring Data JPA实现动态查询的两种方法

    前言 一般在写业务接口的过程中,很有可能需要实现可以动态组合各种查询条件的接口.如果我们根据一种查询条件组合一个方法的做法来写,那么将会有大量方法存在,繁琐,维护起来相当困难.想要实现动态查询,其实就是要实现拼接SQL语句.无论实现如何复杂,基本都是包括select的字段,from或者join的表,where或者having的条件.在Spring Data JPA有两种方法可以实现查询条件的动态查询,两种方法都用到了Criteria API. Criteria API 这套API可用于构建对数据

  • 详解Spring Data JPA动态条件查询的写法

    我们在使用SpringData JPA框架时,进行条件查询,如果是固定条件的查询,我们可以使用符合框架规则的自定义方法以及@Query注解实现. 如果是查询条件是动态的,框架也提供了查询接口. JpaSpecificationExecutor 和其他接口使用方式一样,只需要在你的Dao接口继承即可(官网代码). public interface CustomerRepository extends CrudRepository<Customer, Long>, JpaSpecification

  • Spring Data JPA实现动态条件与范围查询实例代码

    Spring Data JPA为我们提供了Query With Example来实现动态条件查询,当查询条件为空的时候,我们不用做大量的条件判断.但是Query With Example却不支持范围查询(包括日期范围,数值范围查询),本文通过Specification实现了既支持动态条件查询又支持范围查询的方法. 1 实现方式 1.1 范围对象Range定义 import java.io.Serializable; public class Range<E> implements Serial

  • Spring Data JPA 复杂/多条件组合分页查询

    话不多说,请看代码: public Map<String, Object> getWeeklyBySearch(final Map<String, String> serArgs, String pageNum, String pageSize) throws Exception { // TODO Auto-generated method stub Map<String,Object> resultMap=new HashMap<String, Object&

  • Spring Date jpa 获取最新一条数据的实例代码

    #Repository import test.demo.domain.entity.TestEntity; import org.springframework.data.jpa.repository.support.JpaRepositoryImplementation; import org.springframework.stereotype.Repository; @Repository public interface TestEntityRepository extends Jpa

  • Spring Data JPA中 in 条件参数的传递方式

    关于Spring Data JPA中自定义sql 条件的 in参数记录 此文做一个记录,以便以后观看,也希望正在遇到同样问题的同学能有所启发,如果你有更好的方法,或我的做法有问题,请告知下,非常感谢. 下面写两种简单做法,注意下地方: 在此我只记录HQL 中 in 后面参数问题,细节查询方法相关代码就略过,还请见谅 查询接口: /** ** paramString : 自定义 hql ** paramMap : hql中查询条件的参数 ** start:数据开始条数 ** max:最大数据条数

  • Spring MVC结合Spring Data JPA实现按条件查询和分页

    本文实例为大家分享了Android九宫格图片展示的具体代码,供大家参考,具体内容如下 推荐视频:尚硅谷Spring Data JPA视频教程,一学就会,百度一下就有. 后台代码:在DAO层继承Spring Data JPA的PagingAndSortingRepository接口实现的 (实现方法主要在SbglServiceImpl.java类中) 前台表现:用kkpaper表现出来 实现效果: 1.实体类 package com.jinhetech.yogurt.sbgl.entity; im

  • spring data jpa 创建方法名进行简单查询方式

    目录 最常见的做法是 按照规范创建查询方法 支持的规范表达式 spring data jpa 可以通过在接口中按照规定语法创建一个方法进行查询,spring data jpa 基础接口中,如CrudRepository中findOne,save,delete等,那么我们自己怎么按照需要创建一个方法进行查询呢? 最常见的做法是 声明一个接口继承于CrudRepository 或者 PagingAndSortingRepository,JpaRepository,Repository public

  • 使用Spring Data JPA的坑点记录总结

    前言 Spring-data-jpa的基本介绍:JPA诞生的缘由是为了整合第三方ORM框架,建立一种标准的方式,百度百科说是JDK为了实现ORM的天下归一,目前也是在按照这个方向发展,但是还没能完全实现.在ORM框架中,Hibernate是一支很大的部队,使用很广泛,也很方便,能力也很强,同时Hibernate也是和JPA整合的比较良好,我们可以认为JPA是标准,事实上也是,JPA几乎都是接口,实现都是Hibernate在做,宏观上面看,在JPA的统一之下Hibernate很良好的运行. 最近在

  • spring data jpa使用详解(推荐)

    使用Spring data JPA开发已经有一段时间了,这期间学习了一些东西,也遇到了一些问题,在这里和大家分享一下. 前言: Spring data简介: Spring Data是一个用于简化数据库访问,并支持云服务的开源框架.其主要目标是使得对数据的访问变得方便快捷,并支持map-reduce框架和云计算数据服务. Spring Data 包含多个子项目: Commons - 提供共享的基础框架,适合各个子项目使用,支持跨数据库持久化 JPA - 简化创建 JPA 数据访问层和跨存储的持久层

  • Spring data jpa的使用与详解(复杂动态查询及分页,排序)

    一. 使用Specification实现复杂查询 (1) 什么是Specification Specification是springDateJpa中的一个接口,他是用于当jpa的一些基本CRUD操作的扩展,可以把他理解成一个spring jpa的复杂查询接口.其次我们需要了解Criteria 查询,这是是一种类型安全和更面向对象的查询.而Spring Data JPA支持JPA2.0的Criteria查询,相应的接口是JpaSpecificationExecutor. 而JpaSpecifica

随机推荐