美文网首页SpringBoot精选Spring Boot
SpringBoot第五讲扩展和封装Spring Data JP

SpringBoot第五讲扩展和封装Spring Data JP

作者: 孔浩 | 来源:发表于2016-12-16 21:08 被阅读0次

    上一讲讲解了如何使用Spring Data JPA封装一个自己的BaseRespoistory工厂,这个在实际开发中是非常有必要的,今天我们来进一步封装Spring Data JPA的查询。

    Spring Data JPA已经帮助我们很大程度上简化了我们的查询操作,我们甚至只要写一个接口,然后单纯的写一些方法就可以完成各式各样的查询,但是对于我们程序设计人员而言,总希望所有的查询变得更加的简单方便,为了给程序人员进行再一次的封装,Spring Data JPA提供了Specification的方式进行查询,在前面的内容已经演示过这种查询了,但是,我们在使用的过程中发现这种查询异常的繁琐和复杂,接下来的内容就是我们有效的对Specification进行封装来快速实现一些简单的查询操作。当然如果涉及到更为复杂的操作,依然建议写个方法来自己实现。

    封装自己的Specification的实现有很多种方法,我这里只给出了相对简单的一种,而且并没有考虑太复杂的查询,个人感觉过于复杂的查询还不如直接使用SQL或者HQL来处理方便,以下是几个比较重要的类

    /**
     * Created by konghao on 2016/12/15.
     * 操作符类,这个类中存储了键值对和操作符号,另外存储了连接下一个条件的类型是and还是or
     * 创建时通过 id>=7,其中id就是key,>=就是oper操作符,7就是value
     * 特殊的自定义几个操作符(:表示like %v%,b:表示v%,:b表示%v)
     */
    public class SpecificationOperator {
        /**
         * 操作符的key,如查询时的name,id之类
         */
        private String key;
        /**
         * 操作符的value,具体要查询的值
         */
        private Object value;
        /**
         * 操作符,自己定义的一组操作符,用来方便查询
         */
        private String oper;
        /**
         * 连接的方式:and或者or
         */
        private String join;
    
        ...../*省略了getter和setter*/
    }
    

    SpecificationOperator表示操作符类,用来确定查询条件和值。

    接下来创建SimpleSpecification来实现Specification接口,并且根据条件生成Specification对象,因为在最后查询的时候需要这个对象

    package org.konghao.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;
    
    /**
     * Created by konghao on 2016/12/15.
     */
    public class SimpleSpecification<T> implements Specification<T> {
    
        /**
         * 查询的条件列表,是一组列表
         * */
        private List<SpecificationOperator> opers;
    
        public SimpleSpecification(List<SpecificationOperator> opers) {
            this.opers = opers;
        }
    
        @Override
        public Predicate toPredicate(Root<T> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
            int index = 0;
            //通过resultPre来组合多个条件
            Predicate resultPre = null;
            for(SpecificationOperator op:opers) {
                if(index++==0) {
                    resultPre = generatePredicate(root,criteriaBuilder,op);
                    continue;
                }
                Predicate pre = generatePredicate(root,criteriaBuilder,op);
                if(pre==null) continue;
                if("and".equalsIgnoreCase(op.getJoin())) {
                    resultPre = criteriaBuilder.and(resultPre,pre);
                } else if("or".equalsIgnoreCase(op.getJoin())) {
                    resultPre = criteriaBuilder.or(resultPre,pre);
                }
            }
            return resultPre;
        }
    
        private Predicate generatePredicate(Root<T> root,CriteriaBuilder criteriaBuilder, SpecificationOperator op) {
            /*
            * 根据不同的操作符返回特定的查询*/
            if("=".equalsIgnoreCase(op.getOper())) {
                System.out.println(op.getKey()+","+op.getValue());
                return criteriaBuilder.equal(root.get(op.getKey()),op.getValue());
            } else if(">=".equalsIgnoreCase(op.getOper())) {
                return criteriaBuilder.ge(root.get(op.getKey()), (Number)op.getValue());
            } else if("<=".equalsIgnoreCase(op.getOper())) {
                return criteriaBuilder.le(root.get(op.getKey()),(Number)op.getValue());
            } else if(">".equalsIgnoreCase(op.getOper())) {
                return criteriaBuilder.gt(root.get(op.getKey()),(Number)op.getValue());
            } else if("<".equalsIgnoreCase(op.getOper())) {
                return criteriaBuilder.lt(root.get(op.getKey()),(Number)op.getValue());
            } else if(":".equalsIgnoreCase(op.getOper())) {
                return criteriaBuilder.like(root.get(op.getKey()),"%"+op.getValue()+"%");
            } else if("l:".equalsIgnoreCase(op.getOper())) {
                return criteriaBuilder.like(root.get(op.getKey()),op.getValue()+"%");
            } else if(":l".equalsIgnoreCase(op.getOper())) {
                return criteriaBuilder.like(root.get(op.getKey()),"%"+op.getValue());
            } else if("null".equalsIgnoreCase(op.getOper())) {
                return criteriaBuilder.isNull(root.get(op.getKey()));
            } else if("!null".equalsIgnoreCase(op.getOper())) {
                return criteriaBuilder.isNotNull(root.get(op.getKey()));
            } else if("!=".equalsIgnoreCase(op.getOper())) {
                return criteriaBuilder.notEqual(root.get(op.getKey()),op.getValue());
            }
            return null;
        }
    
    }
    

    SimpleSpecification是核心类型,用来根据条件生成Specification对象,这个SimpleSpecification直接存储了具体的查询条件。

    最后我们创建一个SimpleSpecificationBuilder来具体创建SimpleSpecification,这里为了方便调用简单进行了一下设计。

    
    package org.konghao.specification;
    
    import org.springframework.data.jpa.domain.Specification;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * Created by konghao on 2016/12/15.
     */
    public class SimpleSpecificationBuilder<T> {
    
        /**
         * 条件列表
         */
        private List<SpecificationOperator> opers;
    
        /**
         * 构造函数,初始化的条件是and
         */
        public SimpleSpecificationBuilder(String key,String oper,Object value) {
            SpecificationOperator so = new SpecificationOperator();
            so.setJoin("and");
            so.setKey(key);
            so.setOper(oper);
            so.setValue(value);
            opers = new ArrayList<SpecificationOperator>();
            opers.add(so);
        }
    
        public SimpleSpecificationBuilder() {
            opers = new ArrayList<SpecificationOperator>();
        }
    
        /**
         * 完成条件的添加
         * @return
         */
        public SimpleSpecificationBuilder add(String key,String oper,Object value,String join) {
            SpecificationOperator so = new SpecificationOperator();
            so.setKey(key);
            so.setValue(value);
            so.setOper(oper);
            so.setJoin(join);
            opers.add(so);
            return this;
        }
    
        /**
         * 添加or条件的重载
         * @return this,方便后续的链式调用
         */
        public SimpleSpecificationBuilder addOr(String key,String oper,Object value) {
            return this.add(key,oper,value,"or");
        }
    
        /**
         * 添加and的条件
         * @return
         */
        public SimpleSpecificationBuilder add(String key,String oper,Object value) {
            return this.add(key,oper,value,"and");
        }
    
        public Specification generateSpecification() {
            Specification<T> specification = new SimpleSpecification<T>(opers);
            return specification;
        }
    }
    

    现在几个比较重要的类以及实现,接下来看看具体的调用。首先创建一个StudentRepository的接口实现JpaSpecificationExecutor

    
    /**
     * Created by konghao on 2016/12/16.
     * 该接口实现了上一节介绍的BaseRepository和JpaSpecificationExecutor
     * JpaSpecificationExecutor可以通过findAll方法传入SimpleSpecification来进行查询
     */
    public interface StudentRepository extends BaseRepository<Student,Integer>,JpaSpecificationExecutor<Student> {
    
    }
    

    大家注意这个接口中没有任何的查询,基本就是一个空接口,按照原来JPA的处理方式,我们需要为一些简单的查询写一些类似findByUsername的方法。现在我们已经封装了自己的SimpleSpecification,我们来看看测试类如何调用。

    package org.konghao;
    
    import org.junit.Assert;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.konghao.model.Student;
    import org.konghao.repository.StudentRepository;
    import org.konghao.specification.SimpleSpecificationBuilder;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.test.context.junit4.SpringRunner;
    
    import java.util.List;
    
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class DemoApplicationTests {
        @Autowired
        private StudentRepository studentRepository;
    
        @Test
        public void testFind() {
    
            /**
             * 这里的查询表示id大于4或者name中包含a
             * 现在我们发现在SimpleSpecificationBuilder的add或者addOr方法中返回this的好处了
             */
            List<Student> stus = studentRepository.findAll(
                    new SimpleSpecificationBuilder("id",">",4)
                            .addOr("name",":","a")
                            .generateSpecification());
    
            Assert.assertEquals(5,stus.size());
    
        }
    }
    
    

    我们完成了一个不算太复杂的查询,如果你原来认为在接口中写衍生查询的方法太复杂,用现在这种方式是不是简单了许多呢?好了,这一讲就到这里,下一讲我们可以结束Spring Data JPA了。就差两个非常简单的小知识:分页和事务处理。、
    本文的源代码在这里:源代码

    相关文章

      网友评论

        本文标题:SpringBoot第五讲扩展和封装Spring Data JP

        本文链接:https://www.haomeiwen.com/subject/pjqomttx.html