JPA的多表复杂查询

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

 原文连接: https://mp.weixin.qq.com/s/7J6ANppuiZJccIVN-h0T3Qsql

2017-11-10  从小爱喝AD钙 

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

栗子1:mybatis

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

  1. 名字ide

  2. IDpost

  3. 手机号fetch

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

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

  
  
  
  
  1. spa

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 所须要的门槛,但愿你们能够经过个人经验,更方便的与数据库进行交互。

相关文章
相关标签/搜索