详解Jpa动态复杂条件查询,查询指定字段、并包括sum、count、avg等数学运算,包括。。。
Jpa是我⼀直推荐在Springboot及项⽬中使⽤的数据库框架,并由于官⽅的并不是⼗分友好和易⽤的api,导致很多⼈使⽤起来并不⽅便,下⾯就来展⽰⼀下我对api进⾏了封装后的代码。⼤⼤减轻了使⽤难度。
效果展⽰
⾸先我们直接来看最终的结果:
譬如有个entity叫PtActivity,它有⼀个Repository。
1public interface PtActivityRepository extends JpaRepository<PtActivity, Long>,
2 JpaSpecificationExecutor<PtActivity> {
3
4}
十大贫困县继承了JpaSpecificationExecutor后,它拥有了这样⼀个⽅法:
Page<T> findAll(@Nullable Specification<T> var1, Pageable var2);
即传⼊⼀个Specification对象,即可完成条件查询,来看⼀个简单的例⼦。MySpecification就是封装好的⼯具类,能够⼤幅简化jpa构建条件查询的操作。
1 private Page<PtActivity> find(String states, String name, String begin, String end, Pageable pageable) {
2 MySpecification<PtActivity> mySpecification = new MySpecification<>();
3 String[] stateArray = states.split(",");
4
5 if (begin != null) {
6 mySpecification.("createTime", CommonUtil.beginOfDay(begin), true));
7 }
8 if (end != null) {
包粽子的糯米一般情况要泡几个小时9 mySpecification.add(Restrictions.lte("createTime", dOfDay(end), true));
10 }
11
12 mySpecification.add(Restrictions.in("state", Arrays.asList(stateArray), true));
13 mySpecification.add(Restrictions.like("name", name, true));
14 mySpecification.add(Restrictions.eq("deleteFlag", false, true));
15 return ptActivityManager.findAll(mySpecification, pageable);
16 }
该demo构建了⼀个查询createTime⼤于begin,⼩于end,并且state字段的值,在某个数组范围内,并且name字段like⼀个传来的值,并且deleteFlag字段等于false的查询条件。如果哪个字段没传值,就忽略该筛选条件。
这样代码看起来就很容易理解,下⾯看⼀个稍微复杂点的例⼦:
1public void find() {
2 MySpecification<PtActivity> criteriaQueryBuilder = new MySpecification<>();
3 criteriaQueryBuilder.addAll(Restrictions.pickSome("id","state"));
4 //criteriaQueryBuilder.add(Restrictions.sum("id"));
5 //criteriaQueryBuilder.add(Restrictions.max("state"));
6 criteriaQueryBuilder.("createTime", CommonUtil.beginOfDay("2019-05-01"), true));
7 criteriaQueryBuilder.add(Restrictions.lte("createTime", dOfDay("2019-05-31"), true));
8
9 //criteriaQueryBuilder.upBy("state"));
10
11 List<Tuple> tuples = criteriaQueryBuilder.findResult(em, PtActivity.class);
12 for (Tuple tuple : tuples) {
13 Object count = (0);
14 System.out.println(count);
15 }
16 }
该⽅法完成了只查询id、state字段,并且createTime在某个时间范围内的。如果把注释放开,就是查询sum(id),max(state) 并且groupBy state字段。
详细解析
何为Specification
还是回到Jpa的这个接⼝,可以看到,要完成⼀次查询,主要的⼯作就是构建Specification,⽽Specification接⼝中,主要就是⼀个⽅法即toPredicate⽅法。这个⽅法就是构建select * from table wh
ere xxxxx语句的where条件。其他的not、and都是对Specification的⼀些交集、并集,也就是where语句⾥的and、or。
1public interface JpaSpecificationExecutor<T> {
2 Optional<T> findOne(@Nullable Specification<T> var1);
3
4 List<T> findAll(@Nullable Specification<T> var1);
5
6 Page<T> findAll(@Nullable Specification<T> var1, Pageable var2);
7
8 List<T> findAll(@Nullable Specification<T> var1, Sort var2);
9
10 long count(@Nullable Specification<T> var1);
11}
1public interface Specification<T> extends Serializable {
2 long serialVersionUID = 1L;
3
4 static <T> Specification<T> not(Specification<T> spec) {
5 ated(spec);
6 }
做意大利面7
8 static <T> Specification<T> where(Specification<T> spec) {
9 return Specifications.where(spec);
10 }
11
12 default Specification<T> and(Specification<T> other) {
13 return Specificationsposed(this, other, CompositionType.AND);
14 }
15
16 default Specification<T> or(Specification<T> other) {
17 return Specificationsposed(this, other, CompositionType.OR);
18 }
19
20 @Nullable
21 Predicate toPredicate(Root<T> var1, CriteriaQuery<?> var2, CriteriaBuilder var3);
22}
我们可以这样理解,要做的⼀切事情,就是为了构建Predicate对象,该对象组合了N多个查询⼦语句。
所以我们要做的就是根据前端传来的字段构建多个Predicate对象,再将这多个Predicate组装成⼀个Predicate对象,就完成了条件查询的构建。
长沙软件开发公司如果采⽤官⽅api来完成⼀次复杂条件查询,代码可能是下⾯这样的:
1 public void findTemp() {
2 ptActivityManager.findAll(new Specification<PtActivity>() {
3 @Override
4 public Predicate toPredicate(Root<PtActivity> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder
5 criteriaBuilder) {
6 Path idPath = ("id");
7 Predicate predicate1 = criteriaBuilder.equal(idPath, "12345");
8
9 Path statePath = ("state");端午节五彩绳寓意
10 Predicate predicate2 = criteriaBuilder.equal(statePath, "1");
11
12 return criteriaBuilder.and(predicate1, predicate2);
13 }
14 });
15 }
猛⼀看,其实还是挺乱的,什么root、criteriaQuery、criteriaBuilder都是些什么⿁,怎么组建的Predicate,新⼿⼀看,⽐较茫然。下⾯就来解惑⼀下,这些都是什么⿁。
解析原⽣的底层查询
事实上,要完成⼀次条件查询,它的流程是这样的:
1public List<Tuple> findResult(EntityManager entityManager, Class<T> t) {
2 CriteriaBuilder criteriaBuilder = CriteriaBuilder();
3 CriteriaQuery<Tuple> criteriaQuery = ateTupleQuery();
4 Root<T> root = criteriaQuery.from(t);
5
6 if (!selectorList.isEmpty()) {
7 criteriaQuery.multiselect(buildSelections(criteriaBuilder, root));
8 }
9 if (!criterionList.isEmpty()) {
10 upBy(buildGroupBy(root));
11 }
12 criteriaQuery.where(toPredicate(root, criteriaQuery, criteriaBuilder));
13
14 ateQuery(criteriaQuery).getResultList();
15 }
先获取EntityManager,然后从EntityManager中获取CriteriaBuilder,再从CriteriaBuilder中创建⼀个CriteriaQuery,然后将各个条件都组合到CriteriaQuery中,最终通过ateQuery(criteriaQuery).getResultList()来获取到查询结果。
譬如⼀次查询是这样的:select a, b, sum(c) from table where a > 0 and c < 1 group by a
那么a、b、sum(c)都属于CriteriaQuery中的select参数,where后⾯的条件都属于CriteriaQuery的where后的参数,groupBy和having 都属于CriteriaQuery的对应的参数。最终组合成⼀个丰满的CriteriaQuery,并由EntityManager来createQuery并获取结果集。
可以看到⾥⾯有⾮常完整的构建的⽅法。我们要做的就是将select后⾯的组合成Selection对象,where后⾯的组合成Predicate对
象,having、groupBy什么的按照属性类型组合即可。
六年级演讲稿
这些Selection、Predicate对象怎么构建呢,就是靠CriteriaBuilder。
CriteriaBuilder⾥的箭头的⽅法,都是构建Selection的。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论