扩展Spring Data QBE实现动态范围查询
发表于:2026-11-01 作者:千家信息网编辑
千家信息网最后更新 2026年11月01日,Spring Data JPA提供了Query by Example (QBE) 查询技术,实现了动态条件查询,不必再写烦琐的条件判断。但QBE不支持范围查询,本文结合QBE和Specificatio
千家信息网最后更新 2026年11月01日扩展Spring Data QBE实现动态范围查询
Spring Data JPA提供了Query by Example (QBE) 查询技术,实现了动态条件查询,不必再写烦琐的条件判断。但QBE不支持范围查询,本文结合QBE和Specification实现了动态范围查询。
本文以汪云飞-Spring Data JPA实现动态条件与范围查询实例代码为基础修改,利用org.springframework.data.domain.Range替换了自定义实现,支持Matching Any,保持了原代码的基本结构。源码地址 https://github.com/sunjc/heroes-api
实现代码
FieldRange
import org.springframework.data.domain.Range;import org.springframework.data.domain.Range.Bound;import static org.springframework.data.domain.Range.Bound.inclusive;public class FieldRange> { private String field; private Range range; public FieldRange(String field, T lower, T upper) { this.field = field; this.range = of(lower, upper); } private Range of(T lower, T upper) { Bound lowerBound = Bound.unbounded(); Bound upperBound = Bound.unbounded(); if (lower != null) { lowerBound = inclusive(lower); } if (upper != null) { upperBound = inclusive(upper); } return Range.of(lowerBound, upperBound); } public String getField() { return field; } public Range getRange() { return range; }} ExampleSpecification
提取Example的Specification,SimpleJpaRepository内含有此类,是私有的。
import org.springframework.data.domain.Example;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;import static org.springframework.data.jpa.convert.QueryByExamplePredicateBuilder.getPredicate;public class ExampleSpecification implements Specification { private static final long serialVersionUID = 1L; private final Example example; //NOSONAR public ExampleSpecification(Example example) { Assert.notNull(example, "Example must not be null!"); this.example = example; } @Override public Predicate toPredicate(Root root, CriteriaQuery> query, CriteriaBuilder criteriaBuilder) { return getPredicate(root, criteriaBuilder, example); }} RangeSpecification
import org.springframework.data.jpa.domain.Specification;import javax.persistence.criteria.*;import java.util.Optional;public class RangeSpecification> implements Specification { private FieldRange fieldRange; //NOSONAR public RangeSpecification(FieldRange fieldRange) { this.fieldRange = fieldRange; } @Override public Predicate toPredicate(Root root, CriteriaQuery> query, CriteriaBuilder builder) { Optional lower = fieldRange.getRange().getLowerBound().getValue(); Optional upper = fieldRange.getRange().getUpperBound().getValue(); Path path = root.get(fieldRange.getField()); if (lower.isPresent() && upper.isPresent()) { return builder.between(path, lower.get(), upper.get()); } if (lower.isPresent()) { return builder.greaterThanOrEqualTo(path, lower.get()); } if (upper.isPresent()) { return builder.lessThanOrEqualTo(path, upper.get()); } return null; }} 自定义Repository
WiselyRepository接口
import org.itrunner.heroes.repository.specifications.FieldRange;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.util.List;@NoRepositoryBeanpublic interface WiselyRepository extends JpaRepository { //NOSONAR Page findByExampleAndRange(Example example, List> fieldRanges, Pageable pageable); List findByExampleAndRange(Example example, List> fieldRanges);} WiselyRepository实现
import org.itrunner.heroes.repository.WiselyRepository;import org.itrunner.heroes.repository.specifications.ExampleSpecification;import org.itrunner.heroes.repository.specifications.FieldRange;import org.itrunner.heroes.repository.specifications.RangeSpecification;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.util.ArrayList;import java.util.List;import static org.springframework.data.jpa.domain.Specification.where;public class WiselyRepositoryImpl extends SimpleJpaRepository implements WiselyRepository { //NOSONAR public WiselyRepositoryImpl(JpaEntityInformation entityInformation, EntityManager entityManager) { super(entityInformation, entityManager); } @Override public Page findByExampleAndRange(Example example, List> fieldRanges, Pageable pageable) { return findAll(specifications(example, fieldRanges), pageable); } @Override public List findByExampleAndRange(Example example, List> fieldRanges) { return findAll(specifications(example, fieldRanges)); } private Specification specifications(Example example, List> fieldRanges) { boolean allMatching = example.getMatcher().isAllMatching(); Specification byExample = new ExampleSpecification<>(example); List> byRanges = getRangeSpecifications(fieldRanges); return conjunction(byExample, byRanges, allMatching); } private List> getRangeSpecifications(List> fieldRanges) { List> rangeSpecifications = new ArrayList<>(); for (FieldRange fieldRange : fieldRanges) { rangeSpecifications.add(new RangeSpecification<>(fieldRange)); } return rangeSpecifications; } private Specification conjunction(Specification byExample, List> byRanges, boolean allMatching) { Specification specification = where(byExample); for (Specification rangeSpecification : byRanges) { if (allMatching) { specification = specification.and(rangeSpecification); } else { specification = specification.or(rangeSpecification); } } return specification; }} 使用示例
启用WiselyRepositoryImpl:
@EnableJpaRepositories(repositoryBaseClass = WiselyRepositoryImpl.class)调用范围查询:
public Page findHeroes(Hero hero, Date startDate, Date endDate, int minAge, int maxAge, Pageable pageable) { List> fieldRanges = new ArrayList<>(); fieldRanges.add(new FieldRange<>("birthday", startDate, endDate)); fieldRanges.add(new FieldRange<>("age", minAge, maxAge)); return heroRepository.findByExampleAndRange(of(hero), fieldRanges, pageable);}
查询
范围
动态
代码
条件
支持
烦琐
地址
基础
实例
技术
接口
源码
示例
结构
云飞
私有
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
咖啡
互联网络科技
上海喔噻互联网科技有限公司
互联网金融监管科技数据
易互联网科技有限公司
互联网科技金融公司
深圳互联网络科技有限公司
奇酷互联网络科技公司
互联网科技小额贷款
苏州大垚互联网科技
互联网科技大会
山东吉时兔互联网科技有限公司
哈尔滨新华互联网科技学院
互联网和黑科技的看法
互联网对科技的发展
搜狐科技互联网
乌鲁木齐新华智原互联网科技学校
元气玛特互联网科技公司是外包吗
科技互联网企业在行动
山东亿川互联网科技有限责任公司
互联网科技图片
山东特亿宝互联网科技有限公司
互联网科技小额贷款
互联网金融 科技创新
互联网科技博客
汉立互联网科技有限公司联系电话
宁波青枫互联网科技有限公司
北京市互联网新科技
保山互联网科技靠谱吗
宁波允成互联网科技有限公司