扩展Spring Data QBE实现动态范围查询
发表于:2025-12-01 作者:千家信息网编辑
千家信息网最后更新 2025年12月01日,Spring Data JPA提供了Query by Example (QBE) 查询技术,实现了动态条件查询,不必再写烦琐的条件判断。但QBE不支持范围查询,本文结合QBE和Specificatio
千家信息网最后更新 2025年12月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安全错误
数据库的锁怎样保障安全
教育局网络安全进校园
广东广电网络技术笔试试卷
高中可以学软件开发吗
艾尔等法环 无法连接服务器
子目录站点连接不上数据库
开票软件开发编程
3网络安全工程师待遇
如何手动修复微信数据库
北大研究生网络安全
幻塔不同的服务器有什么区别吗
可以输入指令的服务器
如何设置web服务器的权限
如何通过页面删掉数据库数据
网络安全方面没前途
暮云长亭网络安全
服务器 销售
工行软件开发中心校招笔试
php读取数据库记录
什么软件开发最赚钱
卖云服务器有前途吗
打车软件开发哪家便宜
国泰康数据库
国家网络安全证书照片
软件开发的团队职位
sql对比两个数据库的数据
甘肃塔式服务器哪家好
大型数据库作业
不属于网络安全风险的是
数据库列表
郑州人工智能软件开发机构