jpa多条件查询重写Specification的toPredicate方法

来源:互联网 发布:商务通js代码 编辑:程序博客网 时间:2024/05/18 14:46

Spring Data JPA支持JPA2.0的Criteria查询,相应的接口是JpaSpecificationExecutor。Criteria 查询:是一种类型安全和更面向对象的查询 。

这个接口基本是围绕着Specification接口来定义的, Specification接口中只定义了如下一个方法:

Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb); 
  • 1

要理解这个方法,以及正确的使用它,就需要对JPA2.0的Criteria查询有一个足够的熟悉和理解,因为这个方法的参数和返回值都是JPA标准里面定义的对象。

Criteria查询基本概念

Criteria 查询是以元模型的概念为基础的,元模型是为具体持久化单元的受管实体定义的,这些实体可以是实体类,嵌入类或者映射的父类。

CriteriaQuery接口:代表一个specific的顶层查询对象,它包含着查询的各个部分,比如:select 、from、where、group by、order by等注意:CriteriaQuery对象只对实体类型或嵌入式类型的Criteria查询起作用
Root接口:代表Criteria查询的根对象,Criteria查询的查询根定义了实体类型,能为将来导航获得想要的结果,它与SQL查询中的FROM子句类似

1:Root实例是类型化的,且定义了查询的FROM子句中能够出现的类型。

2:查询根实例能通过传入一个实体类型给 AbstractQuery.from方法获得。

3:Criteria查询,可以有多个查询根。

4:AbstractQuery是CriteriaQuery 接口的父类,它提供得到查询根的方法。CriteriaBuilder接口:用来构建CritiaQuery的构建器对象Predicate:一个简单或复杂的谓词类型,其实就相当于条件或者是条件组合。

Criteria查询基本对象的构建

1:通过EntityManager的getCriteriaBuilder或EntityManagerFactory的getCriteriaBuilder方法可以得到CriteriaBuilder对象2:通过调用CriteriaBuilder的createQuery或createTupleQuery方法可以获得CriteriaQuery的实例

3:通过调用CriteriaQuery的from方法可以获得Root实例过滤条件

A:过滤条件会被应用到SQL语句的FROM子句中。在criteria 查询中,查询条件通过Predicate或Expression实例应用到CriteriaQuery对象上。B:这些条件使用 CriteriaQuery .where 方法应用到CriteriaQuery 对象上C:CriteriaBuilder也作为Predicate实例的工厂,通过调用CriteriaBuilder 的条件方( equalnotEqual, gt, ge,lt, le,between,like等)创建Predicate对象。D:复合的Predicate 语句可以使用CriteriaBuilder的and, or andnot 方法构建。     构建简单的Predicate示例:
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
            Predicate p1=cb.like(root.get(“name”).as(String.class), “%”+uqm.getName()+“%”);            Predicate p2=cb.equal(root.get("uuid").as(Integer.class), uqm.getUuid());            Predicate p3=cb.gt(root.get("age").as(Integer.class), uqm.getAge());        构建组合的Predicate示例:           Predicate p = cb.and(p3,cb.or(p1,p2)); 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

下面我们用两个示例代码来更深入的了解:

1.复杂条件多表查询
  • 1
  • 2
//需要查询的对象public class Qfjbxxdz {    @Id    @GeneratedValue(generator = "system-uuid")    @GenericGenerator(name = "system-uuid", strategy = "uuid.hex")    private String id;    @OneToOne    @JoinColumn(name = "qfjbxx")    private Qfjbxx qfjbxx; //关联表    private String fzcc;    private String fzccName;    @ManyToOne    @JoinColumn(name = "criminalInfo")    private CriminalInfo criminalInfo;//关联表    @Column(length=800)    private String bz;    //get/set......}//创建构造Specification的方法//这里我传入两个条件参数因为与前段框架有关,你们写的时候具体自己业务自行决断private Specification<Qfjbxxdz> getWhereClause(final JSONArray condetion,final JSONArray search) {        return new Specification<Qfjbxxdz>() {            @Override            public Predicate toPredicate(Root<Qfjbxxdz> root, CriteriaQuery<?> query, CriteriaBuilder cb) {                List<Predicate> predicate = new ArrayList<>();                Iterator<JSONObject> iterator = condetion.iterator();                Predicate preP = null;                while(iterator.hasNext()){                    JSONObject jsonObject = iterator.next();                    //注意:这里用的root.join 因为我们要用qfjbxx对象里的字段作为条件就必须这样做join方法有很多重载,使用的时候可以多根据自己业务决断                    Predicate p1 = cb.equal(root.join("qfjbxx").get("id").as(String.class),jsonObject.get("fzId").toString());                    Predicate p2 = cb.equal(root.get("fzcc").as(String.class),jsonObject.get("ccId").toString());                    if(preP!=null){                        preP = cb.or(preP,cb.and(p1,p2));                    }else{                        preP = cb.and(p1,p2);                    }                }                JSONObject jsonSearch=(JSONObject) search.get(0);                Predicate p3=null;                if(null!=jsonSearch.get("xm")&&jsonSearch.get("xm").toString().length()>0){                   p3=cb.like(root.join("criminalInfo").get("xm").as(String.class),"%"+jsonSearch.get("xm").toString()+"%");                }                Predicate p4=null;                if(null!=jsonSearch.get("fzmc")&&jsonSearch.get("fzmc").toString().length()>0){                    p4=cb.like(root.join("qfjbxx").get("fzmc").as(String.class),"%"+jsonSearch.get("fzmc").toString()+"%");                }                Predicate preA;                if(null!=p3&&null!=p4){                    Predicate  preS =cb.and(p3,p4);                    preA =cb.and(preP,preS);                }else if(null==p3&&null!=p4){                    preA=cb.and(preP,p4);                }else if(null!=p3&&null==p4){                    preA=cb.and(preP,p3);                }else{                    preA=preP;                }                predicate.add(preA);                Predicate[] pre = new Predicate[predicate.size()];                query.where(predicate.toArray(pre));                return query.getRestriction();            }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64

编写DAO类或接口
dao类/接口 需继承

    public interface JpaSpecificationExecutor<T>  
  • 1

接口;
如果需要分页,还可继承

public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID>  
  • 1

JpaSpecificationExecutor 接口具有方法

    Page<T> findAll(Specification<T> spec, Pageable pageable);  //分页按条件查询      List<T> findAll(Specification<T> spec);    //不分页按条件查询  
  • 1
  • 2
  • 3

方法。 我们可以在Service层调用这两个方法。
两个方法都具有 Specification spec 参数,用于设定查询条件。
Service 分页+多条件查询 调用示例:

studentInfoDao.findAll(new Specification<StudentInfo> () {     public Predicate toPredicate(Root<StudentInfo> root,       CriteriaQuery<?> query, CriteriaBuilder cb) {      Path<String> namePath = root.get("name");      Path<String> nicknamePath = root.get("nickname");      /**          * 连接查询条件, 不定参数,可以连接0..N个查询条件          */      query.where(cb.like(namePath, "%李%"), cb.like(nicknamePath, "%王%")); //这里可以设置任意条查询条件      return null;     }    }, page);   }  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

这里通过CriteriaBuilder 的like方法创建了两个查询条件, 姓名(name)字段必须包含“李”, 昵称(nickname)字段必须包含“王”。
然后通过
连接多个查询条件即可。 这种方式使用JPA的API设置了查询条件,所以不需要再返回查询条件Predicate给Spring Data Jpa,故最后return null;即可。


原文:http://blog.csdn.net/han1196639488/article/details/54909100

原创粉丝点击