SpringDataJpa的Specification查询

来源:互联网 发布:二次元音乐软件 编辑:程序博客网 时间:2024/06/05 04:02

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

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

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

要理解这个方法,以及正确的使用它,就需要对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 的条件方( equal,notEqual, gt, ge,lt, le,between,like等)创建Predicate对象。    D:复合的Predicate 语句可以使用CriteriaBuilder的and, or andnot 方法构建。 

构建简单的Predicate示例:

            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)); 

当然也可以形如前面动态拼接查询语句的方式,比如:

Specification<UserModel> spec = new Specification<UserModel>() {      public Predicate toPredicate(Root<UserModel> root,              CriteriaQuery<?> query, CriteriaBuilder cb) {          List<Predicate> list = new ArrayList<Predicate>();          if(um.getName()!=null && um.getName().trim().length()>0){              list.add(cb.like(root.get("name").as(String.class), "%"+um.getName()+"%"));          }          if(um.getUuid()>0){              list.add(cb.equal(root.get("uuid").as(Integer.class), um.getUuid()));          }          Predicate[] p = new Predicate[list.size()];          return cb.and(list.toArray(p));      }  };

也可以使用

CriteriaQueryPreSpecification<UserModel> spec = new Specification<UserModel>() {      public Predicate toPredicate(Root<UserModel> root,              CriteriaQuery<?> query, CriteriaBuilder cb) {          Predicate p1 = cb.like(root.get("name").as(String.class), "%"+um.getName()+"%");          Predicate p2 = cb.equal(root.get("uuid").as(Integer.class), um.getUuid());          Predicate p3 = cb.gt(root.get("age").as(Integer.class), um.getAge());          //把Predicate应用到CriteriaQuery中去,因为还可以给CriteriaQuery添加其他的功能,比如排序、分组啥的          query.where(cb.and(p3,cb.or(p1,p2)));          //添加排序的功能          query.orderBy(cb.desc(root.get("uuid").as(Integer.class)));          return query.getRestriction();      }  };

ation的toPredicate方法构建更多更复杂的查询方法。甚至如果你想多表链接进行查询,首先在对象上用@onetoone、@onetomany、@manytoone等注解,然后利用root.jion()方法也能做到,还 可以设定链表方式等。具体可以查看相关jpa文档。

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

1.复杂条件多表查询
//需要查询的对象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();            }        };    }public void findByPage(JSONArray condetion,JSONArray search) {    Pageable pageable = ....//构建分页对象     Specification<Qfjbxxdz> spec = getWhereClause(condetion,search)    //利用JpaSpecificationExecutor接口的分页查询方法:    //Page<T> findAll(Specification<T> spec, Pageable pageable);    //这就是上一个文章里我为什么要继承JpaSpecificationExecutor接口的原因    Page<Qfjbxxdz> page = qfjbxxdzRepository.findAll(spec, pageable );}

2.EntityManager的使用

@PersistenceContextprivate EntityManager em;//注入entitymanager//A.利用entitymanager构建Criteria查询//其实和sql一样,就是换成了面向对象的方式。这种方式的好处就是可以防止sql拼写错误。当然这种方式也不是特别直观public List<Qfsqmx> countQfsq(List<Qfsq> list) {    CriteriaBuilder cb = em.getCriteriaBuilder();    CriteriaQuery<Qfsqmx> q = cb.createQuery(Qfsqmx.class);    Root<Qfsqmx> root = q.from(Qfsqmx.class);    List<Predicate> predicate = new ArrayList<>();    //in条件拼接    In<String> in = cb.in(root.get("qfsqid").as(String.class));    for (int i = 0; i < list.size(); i++) {        in.value(list.get(i).getId());    }   //设定select字段    q.multiselect(        root.get("qfmc"),        root.get("qfcc"),        root.get("jldw"),        cb.sum(root.get("sl").as(Integer.class))    );    predicate.add(in);    Predicate[] pre = new Predicate[predicate.size()];    //设定where条件    q.where(predicate.toArray(pre));    //设定groupby条件    q.groupBy(        root.get("qflxid").as(String.class),        root.get("qfccid").as(String.class),        root.get("qfmc").as(String.class),        root.get("qfcc").as(String.class),        root.get("jldw").as(String.class)    );    //设定orderby条件    q.orderBy(cb.asc(root.get("qfmc").as(String.class)));    List<Qfsqmx> rs = em.createQuery(q).getResultList();    return rs;}//B.利用em进行本地sql分页查询public JsonReader selectSrzc(QueryParams queryParams,String qsrq, String jzrq, String zmlx, String zlx, String zhlx, String ssjq, String zfxm) {    StringBuffer hql = new StringBuffer();     hql.append("SELECT to_char(srzc.sj,'yyyy-mm-dd'),ZFZH.ZHBH,ZF.xm,zf.gyjq,decode(srzc.zhlx,'ykt','***','grzh','***'),srzc.zmlx,srzc.zlx,srzc.jsr,srzc.srzc,ye.ye,srzc.sjly FROM ");    hql.append("(SELECT * FROM (");    hql.append("SELECT CRIMINALSACCOUNT AS zhid,RZSJ AS sj,ZHLX,RZLX AS zmlx,RZZLX AS zlx,RZR AS jsr,RZJE AS srzc,'**' AS sjly FROM DZ_PERSON_INCOME WHERE SHZT = '**' ");    hql.append("UNION ALL ");    hql.append("SELECT CRIMINALSACCOUNT AS zhid,CZSJ AS sj,ZHLX,CZLX AS zmlx,CZZLX AS zlx,CZR AS jsr,CZJE AS srzc,'***' AS sjly FROM DZ_PERSON_OUTGOING ");    hql.append(")) srzc ");    hql.append("LEFT JOIN SW_DZ_ZFZH zfzh ON srzc.zhid = zfzh.id ");    hql.append("LEFT JOIN BS_CRIMINALINFO zf ON zfzh.zfid = zf.id ");    hql.append("LEFT JOIN (SELECT * FROM SW_DZ_LSYEJL WHERE TO_CHAR(rq,'yyyy-mm-dd') = ?1) ye ON zf.id = ye.zfid ");    hql.append("WHERE TO_CHAR(sj,'yyyy-mm-dd') >= ?2 AND TO_CHAR(sj,'yyyy-mm-dd') <= ?3 ");    if(StringUtils.isNotEmpty(zmlx)){        hql.append("AND zmlx = '"+zmlx+"'");    }        //....省略    if(StringUtils.isNotEmpty(zfxm)){        String[] list = zfxm.toString().split("[, ]");        if(list.length>1){            hql.append("AND (xm like '%"+list[0]+"%' or xm like '%"+list[1]+"%' or zhbh like '%"+list[1]+"%' or zhbh like '%"+list[1]+"%')");        }else{            hql.append("AND xm like '%"+list[0]+"%' or zhbh like '%"+list[0]+"%'");        }    }    if(StringUtils.isNotEmpty(queryParams.getSidx())){        switch(queryParams.getSidx()){            case "rq":            hql.append(" order by sj ");            break;            case ...//此处省略        }        hql.append(queryParams.getSord());    }else{        hql.append(" order by xm,sj desc");    }    //统计所有数据条数    String countSql = "select count(*) from ("+hql.toString()+")";    Query countQuery = em.createNativeQuery(countSql);    countQuery.setParameter(1, jzrq);    countQuery.setParameter(2, qsrq);    countQuery.setParameter(3, jzrq);    BigDecimal obj = (BigDecimal) countQuery.getSingleResult();    //得到页数    int totalPage = (int) Math.ceil(obj.divide(new BigDecimal(queryParams.getRows())).doubleValue());    //分页查询    Query query = em.createNativeQuery(hql.toString());    int firstIndex = ((queryParams.getPage()-1) * queryParams.getRows());    query.setFirstResult(firstIndex);    query.setMaxResults(queryParams.getRows());    query.setParameter(1, jzrq);    query.setParameter(2, qsrq);    query.setParameter(3, jzrq);    List<Object> list = query.getResultList();    //设定输出参数    JsonReader jsonReader = new JsonReader();    jsonReader.setPage(queryParams.getPage())    .setTotal(totalPage)    .setRecords(obj.intValue());    jsonReader.setRows(list);    return jsonReader;}注意:在使用entitymanager进行自定义sql查询的时候,如果遇到in条件你这样设定参数将是错误的。query.setParameter(1, jzrq);javax.persistence.Query会在参数外围用引号包围起来 然后你的查询就会变成 in ( 'xx,xx,xx')而不是你期望的in ('xx','xx','xx').对于这种情况我们转换思路,特殊处理://通过em获取hibernate的sessionString sql = "SELECT nf,xx,xx FROM xx WHERE nf in (:nf)";Session session = em.unwrap(Session.class);//获取org.hibernate.QueryQuery query = session.createSQLQuery(sql+buffer);if(qjFields.contains(",")){    String[] qjs =  qjFields.split(",");    query.setParameterList("nf", qjs);}else{    query.setParameter("nf",qjFields);}

//获取数据
List rs = query.list();
以上就是关于Specification查询的相关应用,其实 还有很多用法,这就看各位大侠们自行悟道了。欢迎喜欢springDataJpa的同道中人相互指导交流!

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 淘宝账号被覆盖怎么办 淘宝号注册不了怎么办 appleid密码忘了怎么办 蘑菇街用白富美后忘记账号怎么办 不知道宽带账号怎么办 上网用户名忘了怎么办 别人借淘宝号怎么办 不知道ipad密码怎么办 捡的ipad密码怎么办 12306注册名已存在怎么办 忘记网银用户名怎么办 登录用户名忘了怎么办 网银登录错误怎么办 路由器账号忘了怎么办 忘记宽带账号密码怎么办 12123被注册过怎么办 w10不能创建账户怎么办 忘记xp登录密码怎么办 华硕密码忘记了怎么办 电脑账户已停用怎么办 鼠标灯亮不动怎么办 电脑用户名被停用怎么办 电脑截图不清晰怎么办 win10电脑磁盘空间不足怎么办 我的用户名忘记怎么办 12306用户名密码忘记怎么办 电脑开机要密码怎么办 三星账户忘记了怎么办 电脑账户忘记了怎么办 电脑启动要密码怎么办 电脑用户密码忘记了怎么办 微信语音听不懂怎么办 法院迟迟不立案怎么办 dns连不上网怎么办 监控显示无硬盘怎么办 电脑获取不到dns怎么办 打游戏cpu过高怎么办 注册了公司没做账怎么办 工行u盾没电了怎么办 属狗和属鸡相害怎么办 被别人说老实怎么办