JPA多表查询的方法 - 极悦
首页 课程 师资 教程 报名

JPA多表查询的方法

  • 2022-07-20 05:50:01
  • 1900次 极悦

JPA多表查询的方法有哪些?极悦小编来告诉大家。

例子1:

使用实体类 User 中的多个属性进行过滤。

1.名称

2.身份证

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也可以直接写到这个类中,方便参数的接收,主要封装了以下三个参数。

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:

这里有四张桌子。

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来查询符合条件的live。

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

LEFT主要是指最终属性在哪个表,而前面的“actor”代表从活表查询的第一步。比如我给出的例子是查询living中的actor,然后在userdetail中的sex属性之前查询actor中的user表。join.get("userDetail")。get("sex"),这里是取出对应的属性,直到得到想要的属性。接下来的两个属性是一样的。

很多人对jpa有很大的误解。他们认为jpa的多表多条件复杂查询不如mybatis的查询。如果大家对此感兴趣,还可以了解一下什么是JPA框架,希望对大家的学习能够有所帮助。

选你想看

你适合学Java吗?4大专业测评方法

代码逻辑 吸收能力 技术学习能力 综合素质

先测评确定适合在学习

在线申请免费测试名额
价值1998元实验班免费学
姓名
手机
提交