详解Jpa动态复杂条件查询,查询指定字段、并包括sum、count、avg等数学运...
详解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小时内删除。