在本章中,我们将继续我们在 Spring 中处理数据的旅程。如您所知,我们定义的 API 不会持久化或从任何真实数据源读取。我们将通过演示 Spring 强大的数据特性来解决这个问题。我们将使用 Spring Data JPA 和 MySQL 作为我们的数据库。
在本章中,我们将介绍以下主题:
- Introducing Spring Data JPA
- Installing MySQL
- CRUD operations
- Creating database queries
在本章中,我们将继续我们在 Spring 中处理数据的旅程。如您所知,我们定义的 API 不会持久化或从任何真实数据源读取。我们将通过演示 Spring 强大的数据特性来解决这个问题。我们将使用 Spring Data JPA 和 MySQL 作为我们的数据库。
在本章中,我们将介绍以下主题:
Spring Data JPA 是 Spring Data 的一部分。 Spring Data 比 Spring Data JPA 大得多,所以我们将从解释它开始。 Spring Data作为基于Spring的编程,为我们提供了misc底层数据存储的机制。
感谢 Spring Data,我们可以利用不同的数据存储选项。我们可以访问不同的关系或非关系数据库。在本书中,我们将关注 MySQL 数据库。作为父级,Spring 包含多个子项目;其中之一是 Spring Data JPA。
让我们重点介绍一下 Spring Data 提供的一些最重要的特性:
对我们来说,最重要的模块如下:
正如我们所说,Spring Data JPA 包含用于实现 JPA 存储库的组件。借助 Spring Data JPA,可以轻松创建使用数据访问技术的应用程序。
Spring Data JPA 提供了以下一组特性:
由于我们将重点关注 MySQL 数据库作为我们的存储,因此如果您尚未安装它,则必须在系统上安装它。出于我们的目的,MySQL Community Server 将完成这项工作。去https://dev.mysql.com/downloads/mysql/ 下载安装对于您的操作系统:
为您的操作系统执行安装过程:
对于 macOS,MySQL 有两种变体:
我们将使用 DMG 选项作为首选选项。打开 DMG 文件并启动安装包:
按照安装说明进行操作。安装完成后,您将获得 MySQL 实例的 root 凭据:
点击OK确认。关闭安装程序并弹出 DMG 映像。
现在打开 系统偏好 | MySQL:
您将看到您的本地实例状态。如果它没有运行,请启动它:
我们选择不通过系统启动来启动 MySQL。那是你的决定。
在开始安装之前,请确保您已安装 Microsoft Visual C++ 2013 Redistributable Package。如果没有,请从 Microsoft 下载中心安装。
运行设置。为您的操作系统选择安装类型。我们建议您使用 Developer Default。按照为您提供向导的安装说明进行操作。 MySQL 将被安装并启动。
我们将专注于使用通用二进制文件进行安装。我们可以通过下载压缩的 TAR 或使用系统的软件包管理器安装它来获得它。
我们将介绍最常用的软件包管理器。让我们从 YUM (DNF) 开始。启动终端并以特权用户身份运行命令。
使用 YUM 安装 MySQL:
使用 DNF 安装 MySQL:
安装完成后,启动 MySQL 服务器本地实例并启用自动启动:
这一步非常重要!获取你的root密码:
你应该得到这样的东西:
在 /usr/local/mysql 位置提取 TAR 内容。
然后按照以下程序进行:
我们有一个 MySQL 本地服务器实例启动并运行。我们已经准备好进行一些 Spring Data JPA 开发了!
我们需要一个用于我们的应用程序的模式。使用默认的 UTF-8 排序规则创建一个名为 journaler_api 的模式。为此,由您决定如何访问本地 MySQL 服务器实例并创建模式。我们将使用 MySQL Workbench,如以下屏幕截图所示:
查看 SQL 脚本,如以下屏幕截图所示:
应用 SQL 脚本,如以下屏幕截图所示:
要使用 Spring Data JPA,请打开您的 build.gradle 并扩展它:
我们提供了必要的依赖项,还添加了对 MySQL 连接器和 Spring Data JPA 的支持。接下来,我们需要做的是定义我们的数据源。打开 application.properties 文件,根据本地 MySQL 实例添加配置:
对上述代码的解释如下:
构建并运行应用程序。如果您提供了良好的 URL 和正确的凭据,则不会有问题;应用程序将启动。如果出现任何问题,由于此配置,您将能够看到堆栈跟踪。
启动应用程序后,检查表是否出现在 journaler_api 数据库中:
为了能够对我们的数据执行 CRUD 操作,我们必须实现一些代码。我们将进行必要的更改并引入一些新的类。我们现在向您介绍的是每个 Spring 应用程序操作存储在关系数据库中的数据的标准。
使用 NoteRepository 接口创建一个名为 repository 的包,如下所示:
两个接口都继承 CrudRepository。 CrudRepository 为我们带来以下功能:
现在,当我们定义了存储库时,我们必须更新我们的实体:
要将类用作数据库实体,请使用 @Entity 注释。要指定要使用的表名,请使用 @Table 注释,如我们的 Note 类示例中所示。 @Id 注释用于告诉 Spring 我们的 ID 将是什么字段。如您所见,我们使用 UUID2 作为数据的 ID。
我们现在定义 Todo 类的方式与定义 Note 类的方式相同。我们为这两个实体定义了一个存储库并更新了实体本身。我们将继续进行更改并在我们的服务中引入存储库。更新这两个服务以使用存储库,就像我们在以下示例中所做的那样:
Todo 服务的实现方式与 Note 服务完全相同,只是它没有任何文档化代码,因此更易于阅读。我们更新了使用存储库提供的功能的方法。这意味着这也会影响我们的控制器:
让我们试试我们的应用程序。构建它并启动它。我们将检查每个 CRUD 操作。观察最新更改中引入的有效负载的变化。
我们将首先应用 Insert note:
执行 API 调用并插入更多注释。消息的标题和内容将由您决定。
对 API 调用的 TODO 版本执行相同操作:
创建多个 TODO。
写下一个 Note 和您刚刚创建的 TODO 之一的 ID。然后,尝试更新它们每个的 API 调用。更新注释将返回以下结果:
更新时,TODO 将返回以下结果:
让我们看看我们的数据库中现在有什么。首先为 Notes 执行获取 API 调用:
对 TODO 做同样的事情:
如您所见,我们插入的所有项目都在那里,包括更新的项目。
选择要删除的 ID 并执行 API 调用以删除 Note 和 TODO。
先删除一个注释:
然后删除 TODO:
如果您执行获取 API 调用,您将看到已删除的项目丢失。
让我们尝试再更新一次 API 调用。选择要更新的笔记。这一次,只为 Note 设置一个新标题,但忽略消息字段。如果你尝试更新,你会得到一个错误:
我们出现了问题!让我们修复它!要执行成功的更新,您必须提供整个实体!所有未在有效负载中提供以更新 API 调用但使用默认值定义的字段将覆盖我们数据库中的原始值!使用从 API 调用中获得的整个有效负载。不要使用手动填充的实例触发更新!
到目前为止,我们还没有创建任何 data 传输对象 (DTO)。是时候这样做了。我们将介绍另外两个领域。扩展 Note 和 Todo 类。添加创建和修改的字段:
使用这些时间戳,我们将跟踪创建/更新的时间戳。更新这两个字段将是全自动的。用户不需要在每次执行更新操作时都传递它们,但可以看到它。
我们还引入了以下注解:
多亏了这一点,我们将在序列化过程中忽略空字段。使用以下实现创建 NoteDTO:
TodoDTO 会非常相似,如下:
我们有一个包含强制和辅助字段的主构造函数,它们会将 Note 或 Todo 实例转换为其 DTO 等效项。
我们必须更新我们的服务和控制器类。更新您的 NoteService 类以使用 DTO 类型:
对 TodoService 类执行相同的操作:
最后,更新您的控制器。首先更新 NoteController 类:
更改 TodoController 类:
构建并运行您的应用程序。现在尝试使用 API 调用。您现在可以在不传递所有字段的情况下创建实体。注意创建/修改的时间戳。它们会自动更新。
在结束本章之前,我们将向您介绍数据库查询。我们将定义几个 API 调用来触发我们定义的查询。编写查询很简单。有几种方法。我们将介绍最常见的方法。
在第一个示例中,我们将介绍 API 调用,它将查询并返回给我们所有安排在我们提供的日期之后的 TODO。打开 TodoRepository 并扩展它:
我们使用 @Query 注释来定义我们的查询,并将一个参数传递给它。如您所见,此查询将返回所有安排在所提供日期之后的 TODO。现在打开 TodoService 并引入一个新方法:
最后,让我们将它与 TodoController 类联系起来:
我们在这里引入了一个新类。我们引入了一个数据类,因此我们可以将日期传递给 API 调用。创建一个 TodoLaterThanRequest 类并将其定位在控制器包下。该类应该有一个简单的定义:
构建并运行您的应用程序。确保您已插入多个 TODO,并为 schedule 字段设置了正确的日期。如果您尝试 API 调用,您将获得满足我们在查询中定义的条件的所有项目:
我们再举一个查询数据的例子。我们将向您介绍命名查询。我们将使用应用于实体类的 @NamedQuery 注释来定义命名查询。为了让您更好地理解我们在说什么,我们将扩展我们的代码。打开 Note 类并扩展它:
我们定义了一个查询,它将查找所有带有我们通过 API 调用作为参数提供的标题的笔记。将触发查询的方法称为 findByTitle,因此它必须在我们的 NoteRepository 中定义:
剩下的就是通过添加方法将它与 NoteService 连接起来:
并将服务连接到控制器类:
如您所见,我们还为有效负载定义了 NoteFindByTitleRequest 类:
构建并运行您的应用程序。如果您尝试新创建的 API 调用,您应该能够通过 title 搜索 Notes,就像我们所做的那样:
在本章中,我们向您介绍了 Spring Data JPA 和 MySQL。我们创建了与数据库建立通信所需的所有类。我们不仅使 CRUD 操作成为可能,而且还演示了如何为我们的应用程序编写自定义查询。在下一章中,我们将在 Spring 开发中向前迈出一步。我们将通过引入 Spring Security 来限制对 API 某些部分的访问。