Spring Data JPA實現(xiàn)動態(tài)條件與范圍查詢實例代碼
Spring Data JPA
為我們提供了Query With Example
來實現(xiàn)動態(tài)條件查詢,當查詢條件為空的時候,我們不用做大量的條件判斷。但是Query With Example
卻不支持范圍查詢(包括日期范圍,數(shù)值范圍查詢),本文通過Specification
實現(xiàn)了既支持動態(tài)條件查詢又支持范圍查詢的方法。
1 實現(xiàn)方式
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與實現(xiàn)
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
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
IntelliJ?IDEA?2022.2?正式發(fā)布新功能體驗
IntelliJ?IDEA?2022.2為遠程開發(fā)功能帶來了多項質(zhì)量改進,使其更美觀、更穩(wěn)定,新版本還具有多項值得注意的升級和改進,下面跟隨小編一起看看IDEA?2022.2新版本吧2022-08-08Java并發(fā)編程之阻塞隊列(BlockingQueue)詳解
這篇文章主要介紹了詳解Java阻塞隊列(BlockingQueue)的實現(xiàn)原理,阻塞隊列是Java util.concurrent包下重要的數(shù)據(jù)結(jié)構(gòu),有興趣的可以了解一下2021-09-09Java數(shù)組與二維數(shù)組及替換空格實戰(zhàn)真題講解
數(shù)組對于每一門編程語言來說都是重要的數(shù)據(jù)結(jié)構(gòu)之一,當然不同語言對數(shù)組的實現(xiàn)及處理也不盡相同。Java?語言中提供的數(shù)組是用來存儲固定大小的同類型元素,這篇文章主要介紹了Java數(shù)組與二維數(shù)組及替換空格實戰(zhàn)真題講解2022-07-07