文档章节

SpringDataJpa的Specification查询

w
 wangxinxx
发布于 2016/12/07 12:02
字数 2126
阅读 35
收藏 1
点赞 0
评论 0

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

        也可以使用CriteriaQuery来得到最后的Predicate,示例如下:

Specification<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();  
    }  
};

综上所述,我们可以用Specification的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(SONArray 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的使用

@PersistenceContext
private 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的session
String sql = "SELECT nf,xx,xx FROM xx WHERE nf in (:nf)";
Session session = em.unwrap(Session.class);
//获取org.hibernate.Query
Query query = session.createSQLQuery(sql+buffer);
if(qjFields.contains(",")){
    String[] qjs =  qjFields.split(",");
    query.setParameterList("nf", qjs);
}else{
    query.setParameter("nf",qjFields);
}
//获取数据
List<Object> rs = query.list();

以上就是关于Specification查询的相关应用,其实 还有很多用法,这就看各位大侠们自行悟道了。欢迎喜欢springDataJpa的同道中人相互指导交流!

本文转载自:

共有 人打赏支持
w
粉丝 10
博文 57
码字总数 17378
作品 0
呼和浩特
程序员
恒宇少年/querydsl-chapter

#QueryDSL 简书相关系列文章源码 #一、环境配置 ###第一章:Maven环境下如何配置QueryDSL环境 #二、基础 ###第二章:整合QueryDSL与SpringDataJPA ###第三章:使用QueryDSL与SpringDataJPA完...

恒宇少年 ⋅ 04/19 ⋅ 0

第三章:使用QueryDSL与SpringDataJPA完成Update&Delete

我们上一章讲解了有关QueryDsl整合SpringDataJPA完成简单的单表条件查询,采用了两种模式进行查询一种是完全QueryDsl而另外一种则是整合的形式,既然单表的查询已经讲解接下来我们来看看Que...

恒宇少年 ⋅ 2017/07/05 ⋅ 0

第一章:Maven环境下如何配置QueryDSL环境

QueryDSL是一个通用的查询框架,框架的核心原则是创建安全类型的查询,开始QueryDSL仅支持Hibernate(HQL),在不断开源人士加入QueryDSL团队后,陆续发布了针对JPA,JDO,JDBC,Lucene,Hib...

恒宇少年 ⋅ 2017/07/01 ⋅ 0

第五十二章:基于SpringBoot2使用Rest访问MongoDB数据

在之前项目中我们想要读取内的内容需要使用来完成数据的,那如果我们想要通过的形式获取内的数据就更麻烦了,还需要自行去创建对应的控制器,然后使用从内读取出数据后返回给前端。 在上一章...

恒宇少年 ⋅ 04/22 ⋅ 0

第五十章:SpringBoot2.0新特性 - 岂止至今最简单redis缓存集成

自从升级到了版本后集成作为缓存就更为简单了,我们只需要配置相关的链接信息以及使用注解开启缓存,这样我们就直接可以在项目内使用缓存相关的内容。 由于最近这段时间一直在研发公司的持久...

恒宇少年 ⋅ 04/15 ⋅ 0

第四章:使用QueryDSL与SpringDataJPA实现多表关联查询

对于业务逻辑复制的系统来说都存在多表关联查询的情况,查询的返回对象内容也是根据具体业务来处理的,我们本章主要是针对多表关联根据条件查询后返回单表对象,在下一章我们就会针对多表查询...

恒宇少年 ⋅ 2017/07/09 ⋅ 0

恒宇少年/spring-boot-chapter

简书整套文档以及源码解析 专题 专题名称 专题描述 001 Spring Boot 核心技术 讲解SpringBoot一些企业级层面的核心组件 002 Spring Cloud 核心技术 对Spring Cloud核心技术全面讲解 003 Quer...

恒宇少年 ⋅ 04/19 ⋅ 0

第四十九章:SpringBoot2.0新特性 - 你get到WebMvcConfigurer两种配置方式了吗?

初升级版本,在已经使用的系统内还是存在一些兼容性的问题,有很多变化!!!也存在一些过时的方法、配置文件信息以及类,我们在之前版本的中可以使用抽象类来处理相关的配置,由于版本最低支...

恒宇少年 ⋅ 03/17 ⋅ 0

第五十一章:基于SpringBoot2 & MongoDB完成自动化集成

在企业级项目中一般用于存储文档信息、图片资源等,的内容完全是以 字符串的形式进行存储的,所以我们在获取数据时通过简单的 就可以完成与项目内的实体类转换,不过这个过程是自动的,不需要...

恒宇少年 ⋅ 04/16 ⋅ 0

spring data jpa

最近在学习springboot,持久层操作使用了springdatajpa,我有一个疑问,我之前接触过jpa,项目中有一个persistence.xml管理实体类,实体类使用注解,创建entityManager操作数据库既可,大致是...

樱木花道VS康 ⋅ 05/14 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

volatile和synchronized的区别

volatile和synchronized的区别 在讲这个之前需要先了解下JMM(Java memory Model :java内存模型):并发过程中如何处理可见性、原子性、有序性的问题--建立JMM模型 详情请看:https://baike.b...

MarinJ_Shao ⋅ 30分钟前 ⋅ 0

深入分析Kubernetes Critical Pod(一)

Author: xidianwangtao@gmail.com 摘要:大家在部署Kubernetes集群AddOn组件的时候,经常会看到Annotation scheduler.alpha.kubernetes.io/critical-pod"="",以表示这是一个关键服务,那你知...

WaltonWang ⋅ 38分钟前 ⋅ 0

原子性 - synchronized关键词

原子性概念 原子性提供了程序的互斥操作,同一时刻只能有一个线程能对某块代码进行操作。 原子性的实现方式 在jdk中,原子性的实现方式主要分为: synchronized:关键词,它依赖于JVM,保证了同...

dotleo ⋅ 44分钟前 ⋅ 0

【2018.06.22学习笔记】【linux高级知识 14.4-15.3】

14.4 exportfs命令 14.5 NFS客户端问题 15.1 FTP介绍 15.2/15.3 使用vsftpd搭建ftp

lgsxp ⋅ 54分钟前 ⋅ 0

JeeSite 4.0 功能权限管理基础(Shiro)

Shiro是Apache的一个开源框架,是一个权限管理的框架,实现用户认证、用户授权等。 只要有用户参与一般都要有权限管理,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户...

ThinkGem ⋅ 昨天 ⋅ 0

python f-string 字符串格式化

主要内容 从Python 3.6开始,f-string是格式化字符串的一种很好的新方法。与其他格式化方式相比,它们不仅更易读,更简洁,不易出错,而且速度更快! 在本文的最后,您将了解如何以及为什么今...

阿豪boy ⋅ 昨天 ⋅ 0

Python实现自动登录站点

如果我们想要实现自动登录,那么我们就需要能够驱动浏览器(比如谷歌浏览器)来实现操作,ChromeDriver 刚好能够帮助我们这一点(非谷歌浏览器的驱动有所不同)。 一、确认软件版本 首先我们...

blackfoxya ⋅ 昨天 ⋅ 0

线性回归原理和实现基本认识

一:介绍 定义:线性回归在假设特证满足线性关系,根据给定的训练数据训练一个模型,并用此模型进行预测。为了了解这个定义,我们先举个简单的例子;我们假设一个线性方程 Y=2x+1, x变量为商...

wangxuwei ⋅ 昨天 ⋅ 0

容器之查看minikue的environment——minikube的环境信息

执行如下命令 mjduan@mjduandeMacBook-Pro:~/Docker % minikube docker-envexport DOCKER_TLS_VERIFY="1"export DOCKER_HOST="tcp://192.168.99.100:2376"export DOCKER_CERT_PATH="/U......

汉斯-冯-拉特 ⋅ 昨天 ⋅ 0

mysql远程连接不上

设置了root所有hosts远程登录,可是远程登录还是失败,原因可能如下: 登录本地数据库 mysql -uroot -p123456 查询用户表 mysql> select user,host,password from mysql.user; 删除密码为空的...

冰公子 ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部