当前位置: 首页 > 科技观察

JPA的好搭档——QueryDSL

时间:2023-03-12 07:57:47 科技观察

0。前言和MyBatis相比,我更喜欢SpringDataJPA,因为它更符合面向对象的思想。但是,JPA对复杂查询的支持较弱。常见的有两种方式:第一种方式是Repository继承JpaSpecificationExecutor接口。优点是支持复杂的查询,可以避免编译时出现一些语法错误。缺点是语法晦涩难懂,学习成本太高。还有一种方式是直接写SQL,可以通过JdbcTemplate或者@Query注解进行查询。优点是简单,缺点是...先看代码:@Query(value="selectbs.*from"+"(selectt.baseid,t.basesn,t.basecreatorfullname,t.basestatus,t.basesummary,'北京'faultprovince,'北京'faultcity,"+"replace(t.INC_ResponseLevel,'response','')inc_responselevel,t.inc_happentime,t.basecreatedate,t.inc_equipmentmanufacturer,"+"t.inc_ne_name,t.inc_alarm_id,t.site_alerttype,t.alarmlevel,t.inc_alarm_desc,t.baseacceptouttime,t.basedalouttime,"+"decode(t.flagPretreatment,null,'no','notpretreated','no','yes')flagpretreatment,decode(t.flagPretreatment,null,'no','notpretreatment','no','yes')flagpretreatment2,"+"decode(nvl(t.isImportantIncident,0),1,'是','No')isimportantincident,decode(nvl(t.INC_IsEffectOP,0),1,'Yes','No')inc_iseffectop,"+"t.operationdeal,t.t0_deal,t.faultdescription,t.dealguomodo,t.inc_alarm_cleartime,t.clearinctime,"+"t.renewtime,t.baseclosedate,nvl2(t.checkdealresult,'yes','no')checkdealresult,g.jtitem1,g.jtitem2,g.jtitem3,t.reasontype"+"fromT_DEMOt,T_DEMO_MAPg"+"wheret.Sheet_type='传输网络故障处理单'andt.basestatus='archived'"+"andt.baseCloseDate>=:beginTimeandt.baseCloseDate<:endTime"+"and(t.Withdraw_Desc=g.withdraw_descort.alarmname=g.alarmname)"+"unionall"+"selectt.baseid,t.basesn,t.basecreatorfullname,t.basestatus,t.basesummary,'北京'faultprovince,'北京'faultcity,"+"replace(t.INC_ResponseLevel,'response','')inc_responselevel,t.inc_happentime,t.basecreatedate,t.inc_equipmentmanufacturer,"+"t.inc_ne_name,t.inc_alarm_id,t.site_alerttype,t.alarmlevel,t.inc_alarm_desc,t.baseacceptouttime,t.basedealouttime,"+"decode(t.flagPretreatment,null,'no','notpretreated','no','yes')flagpretreatment,decode(t.flagPretreatment,null,'no','notpretreatment','No','是')flagpretreatment2,"+"解码e(nvl(t.isImportantIncident,0),1,'是','否')isimportantincident,decode(nvl(t.INC_IsEffectOP,0),1,'是','否')inc_iseffectop,"+"t.operationdeal,t.t0_deal,t.faultdescription,t.dealguomodo,t.inc_alarm_cleartime,t.clearinctime,"+"t.renewtime,t.baseclosedate,nvl2(t.checkdealresult,'是','否')checkdealresult,w.jtitem1,w.jtitem2,w.jtitem3,t.reasontype"+"fromWF_BMCC_EOMS_ITDealFaultt,(selectdistinctc.value,c.jtitem1,c.jtitem2,c.jtitem3fromWF_Config_EL_00_NetTypec)w"+"其中(t.Sheet_type='test0'ort.Sheet_type='test1'ort.Sheet_type='test2'"+"ort.Sheet_type='test3'ort.Sheet_type='test4')andt.basestatus='已归档'"+"andt.baseCloseDate>=:beginTimeandt.baseCloseDate<:endTime"+"andt.faultclass=w.value"+")bs"+"wherenotexists(selectdp.processbaseidfromT_DEALdp,T_GROUPSdg其中dp.ProcessBaseSchema="+"'DEALFAULT'anddp.processbaseid=bs.baseidanddp.groupid=dg.groupid)",nativeQuery=true)ListfindOne(@Param("beginTime")longbeginTime,@Param("endTime")longendTime);一看,就是直接写SQL的缺点,下面介绍JPA的最佳搭档:QueryDSL,它的语法和SQL一样简单,代码清晰,有代码提示,编译时查错等优点。同时在架构层面实现了读写分离:JPA负责增删改查,QueryDSL负责查询。1.QueryDsl介绍QueryDSL是一个通用的查询框架,专注于通过JavaAPI构建类型安全的SQL查询。QueryDSL可以通过一组通用的查询API,用户可以构建适用于不同类型的ORM框架或SQL的查询语句。也就是说,QueryDSL是一个基于各种ORM框架和SQL的通用查询框架。借助于QueryDSL,它可以在任何支持的ORM框架或SQL平台上使用一个通用的API来构造查询。目前QueryDSL支持的平台包括JPA、JDO、SQL、Mongodb等。二、介绍QueryDSL本文以gradle构建为例,前提是已经引入了SpringDataJpa,JPA可以正常使用。实现'com.querydsl:querydsl-jpa'annotationProcessor'com.querydsl:querydsl-apt:5.0.0:jpa'annotationProcessor'jakarta.persistence:jakarta.persistence-api'引入JPAQueryFactory@ServicepublicclassDemoQueryDSL{@AutowiredqueFactoryprivateJPAQueryFactory;}3。创建JPA实体我们创建两个示例实体类:员工表和部门表。/***StaffTable*/@Data@Entity@Table(name="T_EMP")publicclassEmp{@IdprivateStringid;//ID@ColumnprivateStringname;//Name@ColumnprivateIntegerage;//age@ColumnprivateStringsex;//gender@ColumnprivateStringdepId;//部门ID}/***部门表*/@Data@Entity@Table(name="T_DEP")publicclassDep{@IdprivateStringid;//ID@ColumnprivateStringdepName;//部门名称}经过编译,QueryDSL会帮我们自动生成两个Q类:QEmp、QDep,我们后续的所有查询都会基于这些Q类的。4.简单查询QueryDSL提供了一种类似SQL的面向对象的写法,而且是类型安全的。有了IDEA的语句提示功能,写SQL就很舒服了。请欣赏下面的例子:publicvoidsimpleSql(){QEmpemp=QEmp.emp;//employeetable/***简单的条件查询和排序,相当于sql:*select*fromT_EMP*wherenamelike'Zhang%'年龄>25*按年龄降序排列;*/ListempList=queryFactory.select(emp).where(emp.name.startsWith("Zhang").and(emp.age.gt(25))).orderBy(emp.age.desc())。拿来();/***分组查询,相当于sql:*selecte.dep_id,max(e.age)fromT_EMPe*groupingbye.depId;*/List<元组>list=queryFactory.select(emp.depId,emp.age.max()).groupBy(emp.depId).fetch();/***分页查询,相当于sql:*select*fromT_EMPe*wheree.dep_id='123'*limit2010;*/ListpageList=queryFactory.select(emp).where(emp.depId.eq("123")).offset(20)。限制(10).fetch();}5。复杂查询QueryDSL对复杂查询也有很好的支持,比如多表关联、动态条件查询等。publicvoidcomplexSql(){QEmpemp=QEmp.emp;//员工表QDepdep=QDep.dep;//部门表/***关联查询,相当于sql:*selecte.*fromT_EMPe*leftjoinT_DEPdone.dep_id=d.id*whered.dep_name='财务部'*/ListempList=queryFactory.select(emp).from(emp).leftJoin(dep).on(emp.depId.eq(dep.id)).where(dep.depName.eq("财务部")).fetch();/***嵌套查询,相当于sql:*selecte.*fromT_EMPe*wheree.dep_id=(*selectidformT_DEPwheredep_name='FinanceDepartment'*)*/ListempList1=queryFactory.select(emp).from(emp).where(emp.depId.eq(queryFactory.select(dep.id).from(dep).where(dep.depName.eq("财务部")))).fetch();/***动态条件,别名,取不同的结果:*/BooleanBuildercondition=newBooleanBuilder();条件和(emp.age.goe(25));如果属实){//动态条件condition.and(emp.sex.eq("Male"));}queryFactory.select(emp.name.as("fullname")).where(condition).fetch();//查找更多结果,返回一组//.fetchOne();//查询只返回一个结果,如果返回多个,会抛出异常//.fetchFirst();//返回多个结果时,只取第一个}以上总结是QueryDSL的一个简单应用。写SQL写代码不是很舒服吗?本文中的示例只是一个尝试,希望你能在实践中应用它们,稍后我将更深入地解释QueryDSL的一些高级用法