Spring Data JPA 多条件连表查询最佳实践

背景 本文是 Spring Data JPA 多条件连表查询 文章的最佳实践总结。 解决什么问题? 使用 Spring Data JPA 需要针对多条件进行连表查询的场景不使用原生 SQL 或者 HQL 的时候,仅仅通过 JpaSpecificationExecutor 构造 Specification 动态条件语句来实现类型安全的多条件查询。 说明 相关上下文背景请前往 前文 了解。 这里再提一下接下来示例会用到的场景: 三个实体:作者、书、书评。其中,作者与书是一对多的关系,书与书评是一对一的关系(当然书评与读者的评价是一对多的关系,这里省去,仅用一对一来进行演示即可)。 假设有这样的后台查询条件:作者名称、书的发布时间、书评的评分。(这里每个实体取一个字段进行连表查询演示,其他字段同理)。返回书籍列表以及相关表字段。 【本文所有代码在此】 最佳实践 需要 SELECT 查询的字段,通过单独的 Java Bean 进行映射 利用 JPA 的自动实体映射结果集 @EntityGraph 注解标注返回实体需要 Fetch 的字段 无需再手动针对连表进行 fetch,解决 N+1 问题 JOIN ON 查询条件使用 join().on() 拼接 Join<Object, Object> author = root.join("author"); author.on(cb.equal(author.get("name"), param.getAuthorName())); WHERE 查询条件使用 query.where() 拼接 query.where(cb.equal(root.get("publishTime"), param.getBookPublishTime())); 代码示例 针对 Repository 需要 override 已有 findAll 方法,使用 @EntityGraph 注解 使用 @EntityGraph 注解,标注额外属性需要 fetch // BookJoinRepository....

2022 September 25 · 2 分钟 · Lex Cao

Spring Data JPA 多条件连表查询 (2022 更新)

痛点 项目中使用 Spring Data JPA 作为 ORM 框架的时候,实体映射非常方便。Spring Data Repository 的顶层抽象完全解决单实体的查询,面对单实体的复杂查询,也能使用 JpaSpecificationExecutor<T> 构造 Specification<T> 轻松应对。 而对于后台管理报表查询需求来说,需要进行连表多条件动态查询的时候,就显得无从下手。因为它并不像 MyBatis 一样能够在 XML 文件中写出动态 SQL 语句。 尽管可以使用 EntityManager 动态拼接原生 SQL 语句,但是该方法返回值为 ResultSet ,也就是说查出来的实体映射关系需要手动映射(😢这样不太优雅,已经定义出实体,还需要自己去映射)。 所以,本文的目的是,在现有实体关系的基础上,结合 Specification<T> 记录下 Spring Data JPA 多条件动态连表查询操作,以及其中的踩坑和优化。 想要直接看结论的,请看这篇 Spring Data JPA 动态多条件连表查询最佳实践。 基础操作 那么,让我们开始进入代码操作。【本文所有代码在此】 前置说明 相关依赖 Java 11 SpringBoot 2.4.2 build.gradle plugins { id 'org.springframework.boot' version '2.4.2' id 'io.spring.dependency-management' version '1.0.11.RELEASE' id 'java' } dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' compileOnly 'org.projectlombok:lombok' runtimeOnly 'mysql:mysql-connector-java' annotationProcessor 'org....

2022 September 24 · 8 分钟 · Lex Cao

通过构建全栈待办应用学习 Rust

什么 我想写一篇关于我如何学习Rust的博客。 请原谅我在 2022 年才开始学习这个伟大的编程语言。 为了在实践中学习Rust,我构建了一个全栈待办应用。 你可以在 这里 尝试。相关源码可以在 GitHub 上找到。 现在,我准备写一下它是如何构建的。 如何 首先,和大家一样,我也是从 The Book 中学习 Rust。这是一本不应该跳过入门学习 Rust 的好书。 在学习了一些基本的语法之后,我尝试从零开始使用 Rust 构建一个全栈应用,这是一个使用 Rust tokio 的后端服务和一个使用 Rust WASM(Web Assembly)的前端页面。 我将分别对这两部分做一个简单的介绍。 后端 后台服务是一个的简单的 REST API。使用 actix-web 作为网络框架。 我将写另一篇博客,介绍我如何使用 TDD 开发 Rust 后台服务。 被 Rust 编译器和 Borrow Checker 教育是一次特别的体验。 对于部署,我使用 Supabase 作为 Postgres 服务,使用 Railway 来运行后台服务 docker 镜像。 前端 前端页面有在线和离线数据源,在线是从后台服务器获取数据,而离线是在本地存储。并且有一个按钮来切换它们。 前台由 Rust WASM 和 yew 框架驱动,这是一个类似 React 基于组件构建 Web 应用框架。 如果你熟悉 JSX,你可以在使用 Yew 时感到很自在。 在 Rust 中编写类似 React 的代码体验良好,而且真的很有趣。 但有一些不同之处我想与大家分享,晚点会写一篇博客来谈这个问题,所以敬请关注。 部署的话,是放在 Vercel 上进行托管,用 GitHub Action 来实现自动部署。...

2022 May 2 · 1 分钟 · Lex Cao

Kotlin/Java TDD 开发流程记录

通过使用 Kotlin / Java 中 Junit5 和 Mockito 测试框架,在预约功能中演示 TDD 开发流程。 TDD 介绍 TDD(Test-Driven Development) 是一种开发流程,中文是「测试驱动开发」。用一句白话形容,就是「先写测试再开发」。先写测试除了能确保测试程式的撰写,还有一个好处:有助于在开发初期厘清程式介面如何设计。详细理论知识可以前往 Wiki 了解,这里不再过多介绍。 测试驱动开发 Test Driven Development TDD 开发流程(5步) 术语说明: 红灯 - Failure - 测试用例失败 绿灯 - Success - 测试用例成功 重构 - Refactor - 重构功能代码 具体步骤: 选定一个功能,编写测试用例 执行测试,得到【红灯】 编写满足测试用例的功能代码 再次执行,得到【绿灯】 【重构】代码 小结: 对于每一个功能,在【红灯】-【绿灯】-【重构】间来回循环往复,不断得到完善。 前置工作 代码说明 使用 Kotlin 语言(会有相对应的 Java 代码) 使用到的测试框架 Running: JUnit5 Mock: MockK / Mockito Assertion: Kotest / AssertJ 只涉及 TDD 的具体流程,不涉及单元测试如何编写(可以看 SpringBoot 单元测试各层) 功能介绍 假设一个用户预约的场景。...

2021 May 3 · 2 分钟 · Lex Cao

Kotlin 奇怪的相等现象探究

最近遇到一个平时没怎么关注的 Kotlin 相等问题,决定记录一下探究过程。 事由 以下代码片段 Kotlin 版本 1.3.72。 还原问题代码,已去除业务逻辑部分,仅保留关键代码,片段如下: // 有一个状态枚举 enum class MyState { OK, CANCELED } // 某个处理函数会返回 nullable MyState fun processing(): MyState? { // 假设当前某种情况下返回 取消 这个状态 return MyState.CANCELED } // 在处理状态时 fun handleState() { // 此时编译器推断出类型为 State? val state = processing() if (state == CANCELED) { // 当处理 CANCELED 以下代码没有执行 println("Handle <CANCELED> state") } } 当处理 CANCELED 代码没有执行,原因在于***「import」***。 import javax.print.attribute.standard.JobState.CANCELED // 此处使用静态导入引入了一个其他包中同名的一个静态变量,该变量声明如下 // public static final JobState CANCELED = new JobState (7); 解决方法:...

2020 April 21 · 5 分钟 · Lex Cao