Spring Data JPA
为我们提供了Query With Example
来实现动态条件查询,当查询条件为空的时候,我们不用做大量的条件判断。但是Query With Example
却不支持范围查询(包括日期范围,数值范围查询),本文通过Specification
实现了既支持动态条件查询又支持范围查询的方法。
排版良好地址:Spring Data JPA实现动态条件与范围查询
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
相关推荐
Spring Data JPA实现动态条件与范围查询实例代码 本文主要介绍了使用Spring Data JPA实现动态条件与范围查询实例代码的方法。该方法可以实现既支持动态条件查询又支持范围查询的功能。 首先,我们需要定义一个...
Spring Data JPA 提供了内置的支持,可以在查询方法中通过 `Pageable` 参数实现分页和排序。 5. **Auditing**:Spring Data JPA 提供了审计功能,可以自动记录实体的创建时间和修改时间。这可以通过 `@CreatedDate`...
4. **Specifications**:Spring Data JPA的`Specification`接口允许动态构建查询条件。这在处理复杂查询时非常有用,因为可以基于业务需求动态组合多个条件。 5. **事务管理**:Spring Data JPA集成Spring的事务...
Spring Data JPA API。 Spring Data JPA 开发文档。 官网 Spring Data JPA API。
Spring框架的核心特性包括依赖注入(DI)和面向切面编程(AOP),并且它还提供了对数据库操作的支持,这主要通过Spring Data JPA和Java Persistence API(JPA)实现。 Spring注解是Spring框架中的一大特色,它极大...
'SpringDataJPA从入门到精通'分为12章 内容包括整体认识JPA、JPA基础查询方法、定义查询方法、注解式查询方法、@Entity实例里面常用注解详解、JpaRepository扩展详解、JPA的MVC扩展REST支持、DataSource的配置、乐观...
Spring Data JPA的主要优点在于其高度的开发效率、成熟的语法结构以及与Spring框架的紧密集成。 1. **开发效率极高**: - Spring Data JPA通过提供自动化的 Repository 实现,减少了大量手动编写SQL和DAO层代码的...
Spring Data JPA是Spring框架的一个模块,它简化了Java应用程序对数据库的操作,通过提供自动的repository接口实现。在处理复杂的查询需求时,Spring Data JPA的`Specifications`接口提供了动态构建查询的能力,允许...
本教程将详细介绍 Spring Data JPA 的核心概念与使用方法,帮助开发者从基础入门到实现复杂查询的完整过程。 第一章:Spring Data JPA 入门 Spring Data JPA 简化了 JPA(Java Persistence API)的开发,通过约定...
- **强大的查询支持**:除了简单的 CRUD 方法,Spring Data JPA 还支持基于方法名的复杂查询,甚至可以使用 JPA Querydsl 或 Specification 进行更复杂的查询。 - **事务管理**:Spring Data JPA 结合 Spring 的事务...
Spring Data JPA 是一个强大的框架,它简化了与Java Persistence API (JPA) 的交互,JPA 是Java 开发者用来管理和持久化应用程序数据的一种标准。在这个“Spring Data JPA Demo”项目中,我们将深入探讨如何利用...
在本项目中,我们主要探讨如何手动构建一个基于SpringMVC、Spring Data JPA、Hibernate以及FreeMarker模板引擎的Maven工程,同时实现环境切换功能。这个基础框架为日常开发工作提供了必要的支持。 首先,SpringMVC...
在Java世界中,Spring Data JPA是一个非常流行的框架,它为开发者提供了与关系数据库交互的便利,通过简化JPA(Java Persistence API)的使用。在处理大量数据时,提高性能和效率是至关重要的,这就是`@...
开发者可以根据项目需求选择不同的JPA实现,而SpringDataJPA就是在此之上提供了一层抽象,即Repository层的实现,进一步简化了持久层的代码编写。 SpringBoot作为Spring生态中一个重要的项目,它的目标是简化新...
**SpringBoot整合SpringData JPA** 是一个现代Java开发中的常见技术栈,它结合了Spring Boot的便捷性和Spring Data JPA的数据访问效率。Spring Boot简化了应用的初始搭建以及配置,而Spring Data JPA则是Spring ...
6. **分页和排序**:Spring Data JPA支持在查询结果中进行分页和排序,可以方便地通过Repository接口实现。 7. **存储过程**:Spring Data JPA也支持调用数据库存储过程,并将其结果转换为Java对象。 8. **JPA实体...
Spring Data JPA还支持更复杂的查询,如使用`@Query`注解编写自定义SQL或者HQL,或者使用` Specifications`进行动态查询。此外,它还提供了事务管理、懒加载、级联操作等特性,使得数据访问更加灵活和强大。 在实际...
6. **Integration with Spring Transaction Management**:Spring Data JPA与Spring的事务管理无缝集成,可以方便地进行事务控制。 在实际使用中,我们需要配置Spring Data JPA,这通常涉及到以下步骤: 1. 添加...