zoukankan      html  css  js  c++  java
  • JPA的多表复杂查询

    转 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 所需要的门槛,希望大家可以通过我的经验,更方便的与数据库进行交互。

  • 相关阅读:
    Shell面试题4:扫描网络内存活主机案例
    第三题批量创建特殊要求用户案例
    chpasswd 更简单的更改密码的方式
    30题之第二题
    shell30题之第一题
    2019牛客多校第四场B xor——线段树&&线性基的交
    POJ 2893 M × N Puzzle——八数码有解条件
    2019HDU多校第四场 Just an Old Puzzle ——八数码有解条件
    2019HDU多校第三场F Fansblog——威尔逊定理&&素数密度
    威尔逊定理
  • 原文地址:https://www.cnblogs.com/xmanblue/p/7813026.html
Copyright © 2011-2022 走看看