JPA的多表复杂查询的方法示例

最近工作中由于要求只能用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接口,随后我创建了一个封装条件的类

public class PageParam<T> {
  private Integer pageSize = 10;
  private Integer pageNumber = 1;
  private String searchName;
  private String searchMobile;
  private String searchId;
}

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

Specification<T> specification = new Specification<T>() {

  @Override
  public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
    List<Predicate> list = new ArrayList<Predicate>();

    if (StringUtils.isNotBlank(searchName)) {
      list.add(cb.like(root.get("name").as(String.class), "%" + searchName + "%"));
    }

    if (StringUtils.isNotBlank(searchId)) {
      list.add(cb.equal(root.get("id").as(Long.class), searchId));
    }

    if (StringUtils.isNotBlank(searchMobile)) {
      list.add(cb.like(root.get("mobile").as(String.class), "%" + searchMobile + "%"));
    }

    Predicate[] p = new Predicate[list.size()];
    return cb.and(list.toArray(p));
  };
};

这里因为都是一个表,所以只要root.get(‘N ‘)这个N对应所要查的 属性的名字就好,属性名 属性名 重要的事情说三遍。
再接下来看一组多表的查询

栗子2:

这里有4张表

public class Living {
  Long id;

  @ManyToOne
  @JsonIgnore
  @JoinColumn(name = "actorId", foreignKey = @ForeignKey(name = "none", value =ConstraintMode.NO_CONSTRAINT))
  public Actor actor;

  @ManyToOne
  @JsonIgnore
  @JoinColumn(name = "regionId", foreignKey = @ForeignKey(name = "none", value =ConstraintMode.NO_CONSTRAINT))
  public Region region;
}

public class Actor {
  Long id;

  @OneToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH }, fetch = FetchType.LAZY)
  @JoinColumn(name = "actorId")
  @org.hibernate.annotations.ForeignKey(name = "none")
  List<Living> livings = new ArrayList<>();

  @OneToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH }, fetch = FetchType.LAZY)
  @org.hibernate.annotations.ForeignKey(name = "none")
  @JoinColumn(name = "userDetailId", foreignKey = @ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT))
  UserDetail userDetail;

  @Column(nullable = false)
  @Enumerated(value = EnumType.ORDINAL)
  ActorType actorType = ActorType.A;

  public enum ActorType{
    A,B,C
  }
}

public class UserDetail {
  Long id; 

  @OneToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH }, fetch = FetchType.LAZY)
  @org.hibernate.annotations.ForeignKey(name = "none")
  @JoinColumn(name = "actorId", foreignKey = @ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT))
  Actor actor;

  String truename;
}

public class Region {
  Long id;

  String name;

  @OneToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH }, fetch = FetchType.LAZY)
  @JoinColumn(name = "regionId")
  @org.hibernate.annotations.ForeignKey(name = "none")
  List<Living> Livings;
}

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

public class PageParam<Living> {
  private Integer pageSize = 10;
  private Integer pageNumber = 1;
  private Sex sex;
  private ActorType actortype;
  private Long cityid;

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

Specification<Living> specification = new Specification<Living>() {

  @Override
  public Predicate toPredicate(Root<Living> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
    List<Predicate> list = new ArrayList<Predicate>();

    if (null!=sex) {
      Join<UserDetail, Living> join = root.join("actor", JoinType.LEFT);
      list.add(cb.equal(join.get("userDetail").get("sex"), sex ));
    }

    if (null!=actortype) {
      Join<Actor, Living> join = root.join("actor", JoinType.LEFT);
      list.add(cb.equal(join.get("actorType"), actortype));
    }
    if (null!=cityid) {
      Join<Region, Living> join = root.join("region", JoinType.LEFT);
      list.add(cb.equal(join.get("id"), cityid));
    }

    //Join<A, B> join = root.join("bs", JoinType.LEFT);
    //list.add(cb.equal(join.get("c").get("id"), id));
    Predicate[] p = new Predicate[list.size()];
    return cb.and(list.toArray(p));
  };
};

这里是我对条件进行的封装。jpa 的多条件查询 主要是根据Criteria 为我们提供的方法封装条件,然后根据 给条件定义的位置,再生成sql语句,之后完成查询。

不得不说的地方,在这个多表的查询中以下面这句为例

Join<UserDetail, Living> join = root.join("actor", JoinType.LEFT);
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 所需要的门槛,希望大家可以通过我的经验,更方便的与数据库进行交互。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Spring Data JPA 实现多表关联查询的示例代码

    多表查询在spring data jpa中有两种实现方式,第一种是利用hibernate的级联查询来实现,第二种是创建一个结果集的接口来接收连表查询后的结果,这里介绍第二种方式. 一.一对一映射 实体 UserInfo :用户. 实体 Address:家庭住址. 这里通过外键的方式(一个实体通过外键关联到另一个实体的主键)来实现一对一关联. 实体类 1.实体类 UserInfo.java package com.johnfnash.learn.domain; import java.io.Ser

  • JPA的多表复杂查询的方法示例

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

  • springboot使用JPA时间类型进行模糊查询的方法

    这个问题是我自己开发中遇到的问题  数据库使用的是mysql5.6  字段名称为checkingTime  类型为timestamp 显而易见 存到库中的是保留6位毫秒 即yyyy-MM-dd HH:mm:ss.ssssss  此时需求是精确到分钟的相同时间 不进行存储 这时候就需要进行模糊查询   搜了一圈百度 并没有什么好用的方法 我的bean类定义的是date类型 使用注解将类型更改为timestamp 存入库中 其实在做模糊查询的时候  只需要向持久层传入String类型参数即可 我的做

  • MyBatis Plus 实现多表分页查询功能的示例代码

    在Mybatis Plus 中,虽然IService 接口帮我们定义了很多常用的方法,但这些都是 T 对象有用,如果涉及到 多表的查询,还是需要自定义Vo 对象和自己编写sql 语句,Mybatis Plus提供了一个Page 对象,查询是需要设置其中的 size 字段 和 current 字段的值 一.分页配置 可以直接使用selectPage这样的分页,但返回的数据确实是分页后的数据,但在控制台打印的SQL语句其实并没有真正的物理分页,而是通过缓存来获得全部数据中再进行的分页,这样对于大数据

  • Python_查看sqlite3表结构,查询语句的示例代码

    如下所示: #!/usr/bin/env python3 # -*- coding: utf-8 -*- import sqlite3 conn = sqlite3.connect('test.db') # 创建一个Cursor: cursor = conn.cursor() # 查询记录: conn = sqlite3.connect('calendar.db') cursor = conn.cursor() # 执行查询语句: cursor.execute('select * from pe

  • mybatis Plus 多表联合查询的实现示例

    本文主要介绍了mybatis Plus 多表联合查询,分享给大家,具体如下: //实体类package com.sk.skkill.entity; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import java.io.Serializable; import java.util.D

  • Spring Data JPA 在 @Query 中使用投影的方法示例详解

    Spring Data JPA 在 @Query 中使用投影的方法 关于投影的基本使用可以参考这篇文章:https://www.baeldung.com/spring-data-jpa-projections.下文沿用了这篇文章中的示例代码. 投影的官方文档链接是:https://docs.spring.io/spring-data/jpa/docs/2.6.5/reference/html/#projections (我这里使用的是 2.6.5 的版本). 背景铺垫完毕,接下来开始正文. 最近

  • JS实现线性表的顺序表示方法示例【经典数据结构】

    本文实例讲述了JS实现线性表的顺序表示方法.分享给大家供大家参考,具体如下: 线性表的顺序表示指的是用一组地址连接的存储单元依次存储线性表的数据元素.通常称这种存储结构的线性表为顺序表. 顺序表的特点是以元素在计算机内物理位置相邻来表示数据元素之间的逻辑关系.每一个数据元素的存储位置都和线性表的起始位置相差一个和数据元素在线性表中的位序成正比的常数.也就是说只要确定了存储线性表的起始位置,线性表中的任一元素都可以随机存储,所以说,顺序表是一种随机存取的存储结构. 高级语言中的数组与其相似,所以我

  • Smarty简单生成表单元素的方法示例

    本文实例讲述了Smarty简单生成表单元素的方法.分享给大家供大家参考,具体如下: smarty生成表单元素功能实现的原理是:给smarty一个数组,用于生成和显示菜单或选项,另外在传递一个选项的值,用于默认选择的匹配: 示例如下: php文件:index.php <?php include("smarty_inc.php"); $smarty->assign('cust_ids',array(1000,1001,1002,1003)); $smarty->assig

  • JavaScript实现动态添加Form表单元素的方法示例

    本文实例讲述了JavaScript实现动态添加Form表单元素的方法.分享给大家供大家参考,具体如下: 之前写过类似的文章(如:javascript实现的动态添加表单元素input,button等),现在看来比较初级,弄一个高级的简单的 情景: 后台要上传游戏截图,截图数量不确定,因此使用动态添加input节点的方法去实现这个效果 主要用到的函数有: document.getElementById(); objNode.parentNode; objNode.cloneNode(); objNo

  • SQL语句多表联查的实现方法示例

    目录 前言 三表联查 内连接 外连接 附:多表联合查询训练 总结 前言 最近面试实习生,发现一般来说,公司喜欢通过一些 SQL 语句来考察你对数据库的使用熟不熟悉.所以我来总结一下我在面试中遇到多表联查是怎么联查的. 三表联查 首先来说多表联查的一些知识点. 多表是指等于或者多余三个表以上的数据库查询.多表联查的类型有: 内连接:join,inner join 外连接:left join,left outer join,right join,right outer join,union 交叉连接

随机推荐