专栏电商日志财经减肥爱情
投稿投诉
爱情常识
搭配分娩
减肥两性
孕期塑形
财经教案
论文美文
日志体育
养生学堂
电商科学
头戴业界
专栏星座
用品音乐

在SpringDataJPA中,如何优雅的实现动态查询和连表

  前言
  在ORM框架的选择范围内,一直在讨论两个工具SpringDataJPA和MyBatis,双方的争论各执一词,这里不去争论这些东西,不同的需求、不同的场景采用不同的解决方案是很正常的,孰优孰劣并没有万金油的答案。在这篇文章中我们来切切实实地解决SpringDataJPA中连表查询和动态查询实现复杂的问题。目前在网上搜这两个问题的解决方案大多是JPQL和Specification的方式,JPQL在动态查询上不好实现,Specification在实现的时候太麻烦,而且写出来的代码属实无法评价,也可能是我水平不够,见谅~。其他博客也是抄来抄去的,这里就不提这两种解决方案了,感兴趣的可以自行搜一下。好在现在可以搜到一些QueryDSL相关的博客了,虽然不多,但至少有人尝试新的解决方案,而不是简单的CV,拿来就用。简介
  JPA2。0标准引入了一种新的类型安全的构建查询的方法,可以利用注释在预处理期间生成元模型类,通过生成的元模型类可以构建查询语句。具体可以看CriteriaQueryAPI。
  QueryDSL在编译的时候会自动帮我们生成一些CriteriaQueryAPI会用到的元模型类,然后我们可以直接用这些模型类构建查询,当然QueryDSL不仅仅只有这个作用。解决问题引入依赖
  在Maven的pom。xml文件中引入QueryDSL,目前使用Maven管理项目依赖还是比较多,Gradle的使用方式这里就不介绍了,Quiet用的就是Gradle,需要的话可以看下项目的具体配置,或者私信我也行。dependencygroupIdcom。querydslgroupIdquerydslaptartifactIdversion{querydsl。version}versionscopeprovidedscopedependencydependencygroupIdcom。querydslgroupIdquerydsljpaartifactIdversion{querydsl。version}versiondependencyprojectbuildpluginsplugingroupIdcom。mysema。mavengroupIdaptmavenpluginartifactIdversion1。1。3versionexecutionsexecutiongoalsgoalprocessgoalgoalsconfigurationoutputDirectorytargetgeneratedsourcesjavaoutputDirectoryprocessorcom。querydsl。apt。jpa。JPAAnnotationProcessorprocessorconfigurationexecutionexecutionspluginpluginsbuildproject复制代码注入JPAQueryFactory
  在SpringBoot项目中可以注入BeanJPAQueryFactory方便查询时使用,ConfigurationpublicclassJpaAutoConfig{PersistenceContextprivatefinalEntityManagerentityManager;publicJpaAutoConfig(EntityManagerentityManager){this。entityManagerentityManager;}BeanpublicJPAQueryFactoryjpaQueryFactory(){returnnewJPAQueryFactory(entityManager);}}复制代码生成元模型类
  编译项目,在开发的时候可以使用maven编译一下项目,或者直接运行项目也可以,这步主要是生成一些查询用到的元模型类。生成的模型类中我们用到类名最多的是Q{EntityName}(前缀的Q好像是可以配置的,有需要修改的话可以自己研究下),EntityName是我们的实体类的类名,比如EntitypublicclassUser{}复制代码
  那么生成的元模型类的类名就是QUser。查询
  以下内容中的queryFactory即上文中注入的BeanJPAQueryFactory,本文中只列举几种常用的查询方式,更多查询方式的构建可以看下官网文档(文末附有相关链接)。单表查询
  简单的单表查询直接使用Repository实现即可,动态条件查询在文章后面有构建动态查询条件的方式。QCustomercustomerQCustomer。customer;CustomerbobqueryFactory。selectFrom(customer)。where(customer。firstName。eq(Bob))。fetchOne();复制代码or查询queryFactory。selectFrom(customer)。where(customer。firstName。eq(Bob)。or(customer。lastName。eq(Wilson)));复制代码查询部分字段QEmployeeemployeeQEmployee。employee;ListTupleresultqueryFactory。select(employee。firstName,employee。lastName)。from(employee)。fetch();for(Tuplerow:result){System。out。println(firstNamerow。get(employee。firstName));System。out。println(lastNamerow。get(employee。lastName));}}复制代码查询指定字段并返回指定类型使用setter方法构建查询结果QUseruserQUser。user;ListUserDTOdtosqueryFactory。select(Projections。bean(UserDTO。class,user。firstName,user。lastName))。fetch();复制代码使用字段填充的方式构建查询结果QUseruserQUser。user;ListUserDTOdtosqueryFactory。select(Projections。fields(UserDTO。class,user。firstName,user。lastName))。fetch();复制代码使用类构造方法构建查询结果QUseruserQUser。user;ListUserDTOdtosqueryFactory。select(Projections。constructor(UserDTO。class,user。firstName,user。lastName))。fetch();复制代码不同表之间joinQQuietTeamquietTeamQQuietTeam。quietTeam;QQuietTeamUserquietTeamUserQQuietTeam。quietTeamUser;jpaQueryFactory。selectFrom(quietTeam)。leftJoin(quietTeamUser)。on(quietTeam。id。eq(quietTeamUser。teamId))。where(where)。distinct()。fetch();复制代码join表取别名QCatcatQCat。cat;QCatmatenewQCat(mate);QCatkittennewQCat(kitten);queryFactory。selectFrom(cat)。innerJoin(cat。mate,mate)。leftJoin(cat。kittens,kitten)。fetch();复制代码子查询QDepartmentdepartmentQDepartment。department;QDepartmentdnewQDepartment(d);queryFactory。selectFrom(department)。where(department。size。eq(JPAExpressions。select(d。size。max())。from(d)))。fetch();复制代码QEmployeeemployeeQEmployee。employee;QEmployeeenewQEmployee(e);queryFactory。selectFrom(employee)。where(employee。weeklyhours。gt(JPAExpressions。select(e。weeklyhours。avg())。from(employee。department。employees,e)。where(e。manager。eq(employee。manager))))。fetch();复制代码分页查询
  QueryDSL的分页查询是内存分页,在5。0。0版本已经过期,不建议使用,如果确定数据量不多,影响不大的话可以使用fetchResults方法,在文档中推荐了另一个开源项目:BlazePersistence引入依赖:dependencygroupIdcom。blazebitgroupIdblazepersistenceintegrationquerydslexpressionsartifactIdversion{blazepersistence。version}versionscopecompilescopedependencydependencygroupIdcom。blazebitgroupIdblazepersistenceintegrationhibernate5。6artifactIdversion{blazepersistence。version}versionscoperuntimescopedependency复制代码注入BeanCriteriaBuilderFactoryauthorlinmtConfiguration(proxyBeanMethodsfalse)publicclassJpaConfig{PersistenceUnitprivateEntityManagerFactoryentityManagerFactory;BeanScope(ConfigurableBeanFactory。SCOPESINGLETON)publicCriteriaBuilderFactorycreateCriteriaBuilderFactory(){CriteriaBuilderConfigurationconfigCriteria。getDefault();dosomeconfigurationreturnconfig。createCriteriaBuilderFactory(entityManagerFactory);}}复制代码查询数据:OverridepublicPagedListQuietUserpageUser(NotNullLongdeptId,QuietUserparams,NotNullPageablepage){BooleanBuilderbuilderSelectBuilder。booleanBuilder(params)。getPredicate();builder。and(quietDeptUser。deptId。eq(deptId));returnnewBlazeJPAQueryQuietUser(entityManager,criteriaBuilderFactory)。select(quietUser)。from(quietUser)。leftJoin(quietDeptUser)。on(quietUser。id。eq(quietDeptUser。userId))。where(builder)。orderBy(quietUser。id。desc())。fetchPage((int)page。getOffset(),page。getPageSize());}复制代码结合JPA查询
  SpringDataJPA提供了很多的扩展点,QueryDSL和BlazePersistence构建的查询条件也是支持这些扩展点。在JPA中我们常用的是org。springframework。data。jpa。repository。JpaRepository相关的类作为我们Repository的父类,SpringDataJPA对QueryDSL也是专门提供了一个接口(这应该能算QueryDSL得到了官方认可了吧):org。springframework。data。querydsl。QuerydslPredicateExecutor,那么我们在使用的时候就可以定义一个项目中所有Repository共用的父接口:authorlinmtNoRepositoryBeanpublicinterfaceQuietRepositoryTextendsJpaRepositoryT,Long,QuerydslPredicateExecutorT{}复制代码
  在分页查询的时候就可以使用方法:org。springframework。data。querydsl。QuerydslPredicateExecutorfindAll(com。querydsl。core。types。Predicate,org。springframework。data。domain。Pageable)
  Predicate参数是构建的查询条件实体信息的父类,下面我们会有动态查询条件构建的例子。动态查询
  在项目中我们常常有动态查询的需求,比如前端传了用户名,我们就需要根据用户名进行模糊查询,没有传用户名,就不添加用户名的查询条件,这种需求在SpringDataJPA中实现是比较麻烦的,这也是很多项目不选SpringDataJPA作为项目ORM框架的原因之一,这里就介绍一种比较优雅且可读性较好的方式解决这个问题。
  QueryDSL提供了一种构建查询条件的实体类com。querydsl。core。BooleanBuilder,这个类实现了接口com。querydsl。core。types。Predicate,也就是文章上面提到的SpringDataJPA提供的QueryDSL扩展接口中方法的形参。QEmployeeemployeeQEmployee。employee;BooleanBuilderbuildernewBooleanBuilder();for(Stringname:names){builder。or(employee。name。equalsIgnoreCase(name));}if(id!null){builder。and(employee。id。equals(id))}queryFactory。selectFrom(employee)。where(builder)。fetch();复制代码
  构建动态查询的方式不仅仅只有BooleanBuilder,所有Predicate的子类都可以:
  更优雅地构建动态查询
  在一些后台管理的项目中,统计需求往往会有很多的动态查询的字段,这时候可能就会出现很多的ifelse的代码,这种代码可读性就不是很好了,观察一下Q{EntityName}的字段,相同类型的字段,它们返回的类型其实都是有共同的父类的,这就很好体现了Java的三大特性之一的多态。利用这点我们新建一个构建动态查询的工具类,将动态构建的ifelse隐藏起来,工具里的方法可以根据自己项目的需要自行增删:查询条件构造器。authorlinmtpublicabstractclassSelectBuilderTextendsPredicate{NotNullpublicstaticSelectBooleanBuilderbooleanBuilder(){returnnewSelectBooleanBuilder();}NotNullpublicstaticSelectBooleanBuilderbooleanBuilder(BaseEntityentity){BooleanBuilderbuildernull;if(entity!null){builderentity。booleanBuilder();}returnnewSelectBooleanBuilder(builder);}获取查询条件return查询条件NotNullpublicabstractTgetPredicate();}复制代码构建BooleanBuilder。authorlinmtpublicclassSelectBooleanBuilderextendsSelectBuilderBooleanBuilder{privatefinalBooleanBuilderbuilder;publicSelectBooleanBuilder(){this。buildernewBooleanBuilder();}publicSelectBooleanBuilder(BooleanBuilderbuilder){this。builderbuildernull?newBooleanBuilder():builder;}OverridepublicBooleanBuildergetPredicate(){returnbuilder;}publicSelectBooleanBuilderand(NullablePredicateright){builder。and(right);returnthis;}publicSelectBooleanBuilderandAnyOf(Predicate。。。args){builder。andAnyOf(args);returnthis;}publicSelectBooleanBuilderandNot(Predicateright){returnand(right。not());}publicSelectBooleanBuilderor(NullablePredicateright){builder。or(right);returnthis;}publicSelectBooleanBuilderorAllOf(Predicate。。。args){builder。orAllOf(args);returnthis;}publicSelectBooleanBuilderorNot(Predicateright){returnor(right。not());}publicSelectBooleanBuildernotNullEq(Booleanparam,BooleanPathpath){if(param!null){builder。and(path。eq(param));}returnthis;}publicTextendsNumberComparablelt;?SelectBooleanBuildernotNullEq(Tparam,NumberPathTpath){if(param!null){builder。and(path。eq(param));}returnthis;}publicSelectBooleanBuilderisIdEq(Longparam,NumberPathLongpath){if(param!nullparam0L){builder。and(path。eq(param));}returnthis;}publicTextendsNumberComparablelt;?SelectBooleanBuilderleZeroIsNull(Tparam,NumberPathTpath){if(param!nullparam。longValue()0){builder。and(path。isNull());}returnthis;}publicSelectBooleanBuildernotBlankEq(Stringparam,StringPathpath){if(StringUtils。isNoneBlank(param)){builder。and(path。eq(param));}returnthis;}publicSelectBooleanBuilderwith(NotNullConsumerSelectBooleanBuilderconsumer){if(consumer!null){consumer。accept(this);}returnthis;}publicTextendsEnumTSelectBooleanBuildernotNullEq(Tparam,EnumPathTpath){if(param!null){builder。and(path。eq(param));}returnthis;}publicSelectBooleanBuildernotBlankContains(Stringparam,StringPathpath){if(StringUtils。isNoneBlank(param)){builder。and(path。contains(param));}returnthis;}publicSelectBooleanBuildernotNullEq(Dictdict,QDictqDict){if(dict!nullStringUtils。isNoneBlank(dict。getKey())){builder。and(qDict。eq(dict));}returnthis;}publicSelectBooleanBuildernotNullBefore(LocalDateTimeparam,DateTimePathLocalDateTimepath){if(param!null){builder。and(path。before(param));}returnthis;}publicSelectBooleanBuildernotNullAfter(LocalDateTimeparam,DateTimePathLocalDateTimepath){if(param!null){builder。and(path。after(param));}returnthis;}publicSelectBooleanBuildernotEmptyIn(Collectionlt;?extendsLongparam,NumberPathLongpath){if(CollectionUtils。isNotEmpty(param)){builder。and(path。in(param));}returnthis;}publicSelectBooleanBuilderfindInSet(Longparam,SetPathLong,NumberPathLongpath){if(param!null){builder。and(Expressions。booleanTemplate(FINDINSET({0},{1})0,param,path));}returnthis;}}复制代码使用例子OverridepublicListDocApiGrouplistByProjectIdAndName(LongprojectId,SetLongids,Stringname,Longlimit){if(Objects。isNull(projectId)){returnLists。newArrayList();}BooleanBuilderwhereSelectBooleanBuilder。booleanBuilder()。and(docApiGroup。projectId。eq(projectId))。notEmptyIn(ids,docApiGroup。id)。notBlankContains(name,docApiGroup。name)。getPredicate();JPAQueryDocApiGroupqueryjpaQueryFactory。selectFrom(docApiGroup)。where(where);if(limit!nulllimit0){query。limit(limit);}returnquery。fetch();}复制代码结语
  这篇文章主要是介绍一些比较常用的内容,QueryDSL是基于SQL标准实现了SQL语句的构建,对于不同类型的数据库(MySQL、Oracle等)具有的特性,就需要自己去构建查询方式了,比如上面的findInSet就是MySQL特有的函数,不在SQL标准中,所以要真正用好的话学习成本确实有点高,我也只是了解一点而已。BlazePersistence也是一个很不错的开源项目,目前我也只是把它当成QueryDSL的补充,但其实它也提供了很多查询条件的构建方式,感兴趣的可以自行深入研究哈~最后,再附上相关链接
  QueryDSL官网:querydsl。com
  QueryDSLGithub:github。comquerydslqu
  BlazePersistence官网:persistence。blazebit。comindex。html
  BlazePersistenceGithub:github。comBlazebitbl
  QueryDSL文档:querydsl。comstaticquer
  BlazePersistence文档:persistence。blazebit。comdocumentati
  文档的链接带有版本号,目前是最新的(文章发布时间:20230310),本文就不实时更新这个链接了,后续需要最新的文档可以到官网查询哈。

养生贵在养心当今,人们对健康和保健方面的需求与日俱增,而中医养生更加受到国人的喜爱。中医养生是基于中国传统文化和中医基本理论发展起来的,国医大师孙光荣提出养生第一要义为养心,身心泰然,气血才能一勺猪油等于五副药?常吃猪油是养生还是有害?医生给你揭秘一叠炒青菜,一小把面条,本来是清汤面,但是只要添加一勺猪油,原本清淡的面条马上就变得美味起来,这难道不比清汤面条好吃吗?以上就是猪油青菜面的做法,听起来是不是既便捷又美味?其实这也高尿酸会祸害身体多个器官,这些食物一定要少吃高尿酸,也被称为继高血压高血糖高血脂之后的第四高。但很多人对于高尿酸的危害认识远远不足,认为只要不导致痛风就没问题,尿酸高一点也无所谓。但其实高尿酸会祸害身体多个器官,危害一点也不胰腺,对人体健康有多重要?胰腺若癌变,身体会出现什么异样?对于人体内的器官,可能心肝脾肺肾受到的关注是比较多的,而相比之下,说起胰腺,很多人并不了解。殊不知,它对于身体健康也是非常重要的,若忽视它的健康,一旦出现问题,甚至可能是致命的,今泥沙样胆结石口干口苦隐痛!中医这样疏肝利胆化石排石大家好,我是中医李大夫。今天跟大家分享一个泥沙样胆结石的医案,那么胆结石是我们肝胆系统里面是一个常见的疾病,胆结石经常伴发胆囊炎,出现一些症状。当然也有些人得了胆结石之后什么症状也养成这6个小习惯,身体更健康1坚持运动生命在于运动,经常运动可以促进血液循环增强免疫力。建议每天进行30分钟以上的有氧运动,如快走慢跑等。生命在于运动2保持充足睡眠充足的睡眠是健康的基础,如果长期熬夜或睡眠质肝血虚生斑,心血虚失眠,脾血虚衰老,3个中成药,补血趁年少大家好,我是沈医生。肝血虚生斑,心血虚失眠,脾血虚衰老!如果你的身体老得很快,皮肤变得暗沉,老年斑越长越多,晚上还总是睡不好,感觉身子骨很没劲,明明年纪不大,却像上了年纪一样。这要孙颖莎3比0首秀完胜,中国女乒4人晋级16强,林昀儒3比2淘汰波尔北京时间10月20日消息,2022年世界乒乓球职业大联盟WTT澳门冠军赛结束第二日争夺。女单首轮,世界第一孙颖莎发挥出色,以3比0战胜巴西选手高桥布鲁娜,加上此前过关的陈梦王艺迪陈CBA最新排名!北控4胜1负升榜首,浙江辽宁紧随其后,广东垫底头条创作挑战赛在CBA联赛第五轮率先进行的2场比赛中,马布里率领的北控男篮以10181大胜宁波富邦,豪取三连胜,广州龙狮则以10978大比分战胜福建浔兴,结束本队两连败,宁波富邦与德罗赞轰3769仍无缘今日最佳!对不起,你碰到库里妹夫了北京时间10月20日,NBA常规赛继续进行,联盟今天总共安排了12组对决,德罗赞莫兰特穆雷锡安戈贝尔库兹马西亚卡姆罗齐尔格兰特等球星都有不俗的表现。比如公牛队的德罗赞,在公牛队击败字母哥我极其尊重科比但不愿总提他名字博关注因我们没那么熟直播吧10月20日讯字母哥近日接受了TA雄鹿随队记者EricNehm的采访,并谈到了多个话题。字母哥曾表示自己时至今日都在研究学习科比的比赛以及职业精神,并受益匪浅。对此,字母哥表
首次在液氦上可视化电子动力学用于研究超流体氦表面电子气体的实验布置示意图(不按比例)。来源物理评论B(2023)。DOI10。1103PhysRevB。107。104501由兰开斯特大学领导的一个国际团队发现外媒中芯国际相当于摊牌了随着高端芯片需求暴跌,成熟芯片需求上行,市场的风向开始发生改变了。原来一心研究高端芯片的四大厂商纷纷表示要正式开始宣布扩产28nm生产线,这是要抢大陆晶圆厂的饭碗。要知道,之前成熟跌破30万吨的碳酸锂,何时是底?3月23日,据隆众资讯发布的数据显示,电池级碳酸锂价格下跌10000元吨,均价报280000元吨,工业级碳酸锂下跌10000元吨,均价报24万元吨。这是继3月21日,跌破30万吨大大海深处建粮仓,看广东发展海洋经济的减法加法乘法4314公里的大陆海岸线蜿蜒,1963个海岛如繁星密布,42万平方公里海域辽阔壮观这是向海而兴的广东高质量发展的厚实家底,也是广东向海图强发展现代海洋牧场的最大底气。先天资源优渥让知名电竞手机品牌,黑鲨手机倒闭,电竞手机终将被市场淘汰现在市面上的手机也变得多种多样了,有专门用来拍照的,也有专门用来打游戏的,在当初宣传打游戏的手机中,黑鲨手机是非常知名的,当初市面上的各种风向,包括黑鲨的领导人都在有意无意的透露,近1000家源头工厂超10万件展品,第十五届全球自有品牌产品亚洲展在南京火热进行中现代快报讯(通讯员博轩记者杨晓冬)零售人的采购季到了!3月22日,第十五届全球自有品牌产品亚洲展(PLF)暨第四届零售生鲜食材展(FMR)在南京国际博览中心开幕。现代快报记者了解到迎接未来2023年后产品经理的五大关键能力需求2023年后,国内的产品经理就业形势将会有很大的变化。在这篇文章中,我将从以下几个方面进行分析。一人工智能技术对产品经理职业的影响随着人工智能技术的发展,产品经理职业将会面临一定的2023湖南车展4月28日在长沙开幕布展规模超6万平方米,参展品牌100余个湖南日报3月23日讯(全媒体记者彭可心张福芳)今天上午,2023湖南汽车展览会暨长沙市汽车消费节新闻发布会在长沙召开。记者从会上获悉,本届车展以车享美好生活为主题,将于4月28日至忆表演艺术家管宗祥一个追求极致的电影疯子中国文化报记者刘源隆知名表演艺术家管宗祥于1月13日凌晨逝世,享年100岁。他是北京电影制片厂建厂后的第一代演员,人称管叔。1942年至2019年,管宗祥在七十七载光阴中为数代观众德国人间清醒不装了,继续用俄能源,普京笑了,美沦为孤家寡人欧洲正在通过能源支付为克里姆林宫的战争机器提供资金,而美国沦为了孤家寡人,普京在偷着乐。德国政府宣布将继续使用俄罗斯的天然气,这引起了一些国际社会的关注。对此,德国政府表示,他们认回国的谷爱凌,又被骂了01hr冬奥结束后,谷爱凌就宣布,自己将回美国的斯坦福大学读书。如今时隔一年,她终于回来了。前两天,谷爱凌晒出自己的健身照。肌肉紧实,马甲线迷人。令人意外的是,照片的文案为到家了。
友情链接:快好找快生活快百科快传网中准网文好找聚热点快软网