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接口,随后我创建了一个封装条件的类
publicclassPageParam{ privateIntegerpageSize=10; privateIntegerpageNumber=1; privateStringsearchName; privateStringsearchMobile; privateStringsearchId; }
由于我这个方法是直接分页的所以pageNumber和pageSize也可以直接写入到这个类中,用于方便接收参数,主要是对下面3个参数的封装
Specificationspecification=newSpecification (){ @Override publicPredicatetoPredicate(Root root,CriteriaQuery>query,CriteriaBuildercb){ List list=newArrayList (); 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=newPredicate[list.size()]; returncb.and(list.toArray(p)); }; };
这里因为都是一个表,所以只要root.get(‘N‘)这个N对应所要查的属性的名字就好,属性名属性名重要的事情说三遍。
再接下来看一组多表的查询
栗子2:
这里有4张表
publicclassLiving{ Longid; @ManyToOne @JsonIgnore @JoinColumn(name="actorId",foreignKey=@ForeignKey(name="none",value=ConstraintMode.NO_CONSTRAINT)) publicActoractor; @ManyToOne @JsonIgnore @JoinColumn(name="regionId",foreignKey=@ForeignKey(name="none",value=ConstraintMode.NO_CONSTRAINT)) publicRegionregion; } publicclassActor{ Longid; @OneToMany(cascade={CascadeType.PERSIST,CascadeType.MERGE,CascadeType.REFRESH},fetch=FetchType.LAZY) @JoinColumn(name="actorId") @org.hibernate.annotations.ForeignKey(name="none") Listlivings=newArrayList<>(); @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)) UserDetailuserDetail; @Column(nullable=false) @Enumerated(value=EnumType.ORDINAL) ActorTypeactorType=ActorType.A; publicenumActorType{ A,B,C } } publicclassUserDetail{ Longid; @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)) Actoractor; Stringtruename; } publicclassRegion{ Longid; Stringname; @OneToMany(cascade={CascadeType.PERSIST,CascadeType.MERGE,CascadeType.REFRESH},fetch=FetchType.LAZY) @JoinColumn(name="regionId") @org.hibernate.annotations.ForeignKey(name="none") List Livings; }
现在要根据userdetai种的sexactor中的actortype还有region的id为条件查询出满足条件的living。
publicclassPageParam{ privateIntegerpageSize=10; privateIntegerpageNumber=1; privateSexsex; privateActorTypeactortype; privateLongcityid;
首先我还是封装了这样一个类,但是这里的泛型我是直接给到了想要的查询结果的泛型,接下来因为这里涉及到了一个多表的查询所以上面的单表查询的例子已经不适合这个查询了,但是Criteria的join方法给我们提供了一个模式
Specificationspecification=newSpecification (){ @Override publicPredicatetoPredicate(Root root,CriteriaQuery>query,CriteriaBuildercb){ List list=newArrayList (); if(null!=sex){ Join join=root.join("actor",JoinType.LEFT); list.add(cb.equal(join.get("userDetail").get("sex"),sex)); } if(null!=actortype){ Join join=root.join("actor",JoinType.LEFT); list.add(cb.equal(join.get("actorType"),actortype)); } if(null!=cityid){ Join join=root.join("region",JoinType.LEFT); list.add(cb.equal(join.get("id"),cityid)); } //Joinjoin=root.join("bs",JoinType.LEFT); //list.add(cb.equal(join.get("c").get("id"),id)); Predicate[]p=newPredicate[list.size()]; returncb.and(list.toArray(p)); }; };
这里是我对条件进行的封装。jpa的多条件查询主要是根据Criteria为我们提供的方法封装条件,然后根据给条件定义的位置,再生成sql语句,之后完成查询。
不得不说的地方,在这个多表的查询中以下面这句为例
Joinjoin=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所需要的门槛,希望大家可以通过我的经验,更方便的与数据库进行交互。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。