转 JPA的多表复杂查询:详细篇

原文链接: https://mp.weixin.qq.com/s/7J6ANppuiZJccIVN-h0T3Q

2017-11-10 从小爱喝AD钙 

最近工作中由于要求只能用hibernate+jpa 与数据库进行交互,在简单查询中,jpa继承CrudRepository接口 ,然后利用jpa的方法命名规范进行jpql查询,然而在进行复杂查询时,需要继承JpaSpecificationExecutor接口利用Specification进行复杂查询,由于我自己就遇到了这一问题,查了好多资料,虽然有方法,但是都没有一个详细的讲解,以至于知道方法而不能很好的利用jpa复杂查询的方便之处。我将举几个栗子,来详细的说一下我自己在使用jpa多表复杂查询的场景和想法。

栗子1:

以一个实体类User中的几个属性进行筛选。

  1. 名字

  2. ID

  3. 手机号

这是一个单表的多条件复杂查询,由于是在几个属性中进行筛选,其中的属性的个数不知道有多少个,所以只需要利用Specification 查询就可以很方便的实现这个需求。 下面请看代码: 场景:页面上通过条件筛选,查询用户列表

这里有3个条件 在页面上我设置的id分别为searchName,searchId,searchMobile。 由于这个是user表 所以userRepository 继承JpaSpecificationExecutor接口,随后我创建了一个封装条件的类

  1. public class PageParam<T> {
  2.    private Integer pageSize = 10;
  3.    private Integer pageNumber = 1;
  4.    private String searchName;
  5.    private String searchMobile;
  6.    private String searchId;
  7. }

由于我这个方法是直接分页的 所以pageNumber 和pageSize 也可以直接写入到这个类中,用于方便接收参数,主要是对下面3个参数的封装

  1. Specification<T> specification = new Specification<T>() {
  2.    @Override
  3.    public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
  4.        List<Predicate> list = new ArrayList<Predicate>();
  5.        if (StringUtils.isNotBlank(searchName)) {
  6.            list.add(cb.like(root.get("name").as(String.class), "%" + searchName + "%"));
  7.        }
  8.        if (StringUtils.isNotBlank(searchId)) {
  9.            list.add(cb.equal(root.get("id").as(Long.class), searchId));
  10.        }
  11.        if (StringUtils.isNotBlank(searchMobile)) {
  12.            list.add(cb.like(root.get("mobile").as(String.class), "%" + searchMobile + "%"));
  13.        }
  14.        Predicate[] p = new Predicate[list.size()];
  15.        return cb.and(list.toArray(p));
  16.    };
  17. };

这里因为都是一个表,所以只要root.get('N ')这个N对应所要查的 属性的名字就好,属性名 属性名 重要的事情说三遍。

再接下来看一组多表的查询

栗子2:

这里有4张表

  1. public class Living {
  2.    Long id;
  3.    @ManyToOne
  4.    @JsonIgnore
  5.    @JoinColumn(name = "actorId", foreignKey = @ForeignKey(name = "none", value =ConstraintMode.NO_CONSTRAINT))
  6.    public Actor actor;
  7.    @ManyToOne
  8.    @JsonIgnore
  9.    @JoinColumn(name = "regionId", foreignKey = @ForeignKey(name = "none", value =ConstraintMode.NO_CONSTRAINT))
  10.    public Region region;
  11. }
  1. public class Actor {
  2.    Long id;
  3.    @OneToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH }, fetch = FetchType.LAZY)
  4.    @JoinColumn(name = "actorId")
  5.    @org.hibernate.annotations.ForeignKey(name = "none")
  6.    List<Living> livings = new ArrayList<>();
  7.  @OneToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH }, fetch = FetchType.LAZY)
  8.    @org.hibernate.annotations.ForeignKey(name = "none")
  9.    @JoinColumn(name = "userDetailId", foreignKey = @ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT))
  10.    UserDetail userDetail;
  11.  @Column(nullable = false)
  12.    @Enumerated(value = EnumType.ORDINAL)
  13.    ActorType actorType = ActorType.A;
  14.    public enum ActorType{
  15.        A,B,C
  16.    }
  17. }
  1. public class UserDetail {
  2.    Long id;
  3.  @OneToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH }, fetch = FetchType.LAZY)
  4.    @org.hibernate.annotations.ForeignKey(name = "none")
  5.    @JoinColumn(name = "actorId", foreignKey = @ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT))
  6.    Actor actor;
  7.    String truename;
  8. }
  1. public class Region {
  2.    Long id;
  3.    String name;
  4.    @OneToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH }, fetch = FetchType.LAZY)
  5.    @JoinColumn(name = "regionId")
  6.    @org.hibernate.annotations.ForeignKey(name = "none")
  7.    List<Living> Livings;
  8. }

现在要根据userdetai 种的 sex actor中的actortype 还有 region的id 为条件查询出满足条件的living。

  1. public class PageParam<Living> {
  2.    private Integer pageSize = 10;
  3.    private Integer pageNumber = 1;
  4.    private Sex sex;
  5.    private ActorType actortype;
  6.    private Long cityid;
  7. }    

首先我还是封装了这样一个类,但是这里的泛型 我是直接给到了想要的查询结果的泛型,接下来 因为这里涉及到了一个 多表的查询 所以上面的单表查询的例子 已经不适合这个查询了,但是Criteria 的join方法 给我们提供了一个模式

  1. Specification<Living> specification = new Specification<Living>() {
  2. @Override
  3.    public Predicate toPredicate(Root<Living> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
  4.        List<Predicate> list = new ArrayList<Predicate>();
  5.        if (null!=sex) {
  6.            Join<UserDetail, Living> join = root.join("actor", JoinType.LEFT);
  7.            list.add(cb.equal(join.get("userDetail").get("sex"),  sex ));
  8.        }
  9.        if (null!=actortype) {
  10.            Join<Actor, Living> join = root.join("actor", JoinType.LEFT);
  11.            list.add(cb.equal(join.get("actorType"),  actortype));
  12.        }
  13.        if (null!=cityid) {
  14.            Join<Region, Living> join = root.join("region", JoinType.LEFT);
  15.            list.add(cb.equal(join.get("id"), cityid));
  16.        }
  17.        //Join<A, B> join = root.join("bs", JoinType.LEFT);
  18.        //list.add(cb.equal(join.get("c").get("id"), id));
  19.        Predicate[] p = new Predicate[list.size()];
  20.        return cb.and(list.toArray(p));
  21.    };
  22. };

这里是我对条件进行的封装。jpa 的多条件查询 主要是根据Criteria 为我们提供的方法封装条件,然后根据 给条件定义的位置,再生成sql语句,之后完成查询。 不得不说的地方,在这个多表的查询中以下面这句为例

  1. Join<UserDetail, Living> join = root.join("actor", JoinType.LEFT);
  2. list.add(cb.equal(join.get("userDetail").get("sex"),  sex ));

jointype.LEFT主要是说最终的这个属性 是在哪个表中, 而前面的 “actor” 则表示 从living表中 查询的 第一步的查询,比如我给出的例子 是要查询出 living 中的 actor 然后是actor 中的userdetail 之后才是 userdetail中的 sex属性 所以下面的join.get("userDetail").get("sex") ,这里就是get出相应的属性,一直到你得到想要的属性为止。 接下来的两个属性 也同理, 许多人多jpa 有很大的误解,认为jpa 的多表,多条件复杂查询,不如mybatis的查询,在之前我也是这么觉得,但自从通过jpa 实现了这个多表多条件的复杂查询之后,我觉得hibernate的复杂查询 不逊于mybatis ,尤其是对sql 语句不是很精通的码农,虽然hibernate的门槛较高可jpa 恰恰降低了hibernate 所需要的门槛,希望大家可以通过我的经验,更方便的与数据库进行交互。

JPA的多表复杂查询的更多相关文章

  1. Spring Data JPA 实现多表关联查询

    本文地址:https://liuyanzhao.com/6978.html 最近抽出时间来做博客,数据库操作使用的是 JPA,相对比 Mybatis 而言,JPA 单表操作非常方便,增删改查都已经写好 ...

  2. 序列化表单为json对象,datagrid带额外参提交一次查询 后台用Spring data JPA 实现带条件的分页查询 多表关联查询

    查询窗口中可以设置很多查询条件 表单中输入的内容转为datagrid的load方法所需的查询条件向原请求地址再次提出新的查询,将结果显示在datagrid中 转换方法看代码注释 <td cols ...

  3. JPA使用nativequery多表关联查询返回自定义实体类

    本文为JPA的学习采坑,如有问题欢迎指正. JPA官方推荐的多表关联查询使用不便,接触的有些项目可能会使用JPA 做简单查询,Mybaits做复杂查询.所以想要寻找一种好用的解决方案. JPA多表关联 ...

  4. SpringBoot中使用Spring Data Jpa 实现简单的动态查询的两种方法

    软件152 尹以操 首先谢谢大佬的简书文章:http://www.jianshu.com/p/45ad65690e33# 这篇文章中讲的是spring中使用spring data jpa,使用了xml ...

  5. SpringBoot12 QueryDSL02之利用QueryDSL实现多表关联查询

    1 业务需求 有的系统业务逻辑比较复杂,存在着多表关联查询的的情况,查询的内容不仅仅是单张表的的内容而是多张表的字段组合而成的,直接使用SplringDataJPA实现是比较复杂的,但是如果使用Que ...

  6. 【hql】spring data jpa中 @Query使用hql查询 问题

    spring data jpa中 @Query使用hql查询 问题 使用hql查询, 1.from后面跟的是实体类 不是数据表名 2.字段应该用实体类中的字段 而不是数据表中的属性 实体如下 hql使 ...

  7. spring data jpa 使用方法命名规则查询

    按照Spring Data JPA 定义的规则,查询方法以findBy开头,涉及条件查询时,条件的属性用条件关键字连接,要注意的是:条件属性首字母需大写.框架在进行方法名解析时,会先把方法名多余的前缀 ...

  8. SQL多表连接查询(详细实例)

    转载博客:joeleo博客(http://www.xker.com/page/e2012/0708/117368.html) 本文主要列举两张和三张表来讲述多表连接查询. 新建两张表: 表1:stud ...

  9. SQL多表连接查询

    SQL多表连接查询 本文主要列举两张和三张表来讲述多表连接查询. 新建两张表: 表1:student  截图如下: 表2:course  截图如下: (此时这样建表只是为了演示连接SQL语句,当然实际 ...

随机推荐

  1. Spring ActiveMQ Caused By: javax.jms.IllegalStateException: Connection closed

    根据 http://www.cnblogs.com/yshyee/p/7448808.html 进行JMS操作时,发送跟监听放到不同的项目中进行时,出现以下异常信息: org.springframew ...

  2. python unittest case运行失败重试

    因为使用unittest进行管理case的运行.有时case因为偶然因素,会随机的失败.通过重试机制能够补充保持case的稳定性.查阅资料后发现,python的unittest自身无失败重试机制,可以 ...

  3. SpringMVC学习笔记二:常用注解

    转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6831976.html  参考:http://www.cnblogs.com/leskang/p/5445698 ...

  4. cocos2d-js 调试办法 断点调试 Android真机调试

    一 使用浏览器chrome打开程序,进行调试.跟普通js程序一样. 要么自行搭建服务器,利用python脚本,或者用其他服务器程序(LAMP或XAMPP).然后用浏览器打开服务器地址. 要么直接使用c ...

  5. hbuilder mui uploader图片上传到服务器完整版(ASP.NET)

    html布局,比较简单,模仿微信的: <div class="dynamic_images"> <ul> <!--<li><img  ...

  6. Dom4j完整教程,操作XML教程

    目录 1.DOM4J简介 2.XML文档操作1 2.1.读取XML文档: 2.2.获取根节点 2.3.. 新增一个节点以及其下的子节点与数据 2.4. 写入XML文件 2. 5. 遍历xml节点 2. ...

  7. Javascript调试利器console的使用

    一.Console API Console.assert() 判断第一个参数是否为真,false的话抛出异常并且在console输出相应信息. Console.count() 以参数为标识记录调用的次 ...

  8. 内存问题排查工具 --- valgrind

    1. 概述 2. Valgrind 3. 内存泄漏监测 3.1. 示例代码 3.2. 编译它 3.3. 用Valgrind监测进程的内存泄漏 4. 悬挂指针 4.1. 示例代码 4.2. Valgri ...

  9. Java进制转换, 数据类型, 运算符

    1:进制转换 转换规则: 先把数据的每一位上的系数乘以对应基数的次幂(低位从零开始),然后相加即可 十进制到其他进制 规则:除基取余,直到商为0,最后将余数反转 十进制到二进制: 除2取余,直到商为0 ...

  10. iOS 关于 GIF 图片那点事

    前言 前几天我们项目组的群里提了这么一件事情:在我们的应用中存储动态的GIF图到相册,保存的图片变成了静态图片.而微博则能正确保存,可见这并不是一个技术不可实现的.前不久刚好看了苹果关于ImageIO ...