Quarkus实践指南:构建新一代的Kubernetes原生Java微服务
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

第4章 数据持久化开发

4.1 使用Hibernate ORM和JPA实现数据持久化

4.1.1 前期准备

本案例需要使用 PostgreSQL 数据库,安装、部署数据库的方式有两种,第 1 种是通过Docker容器来安装、部署PostgreSQL数据库,第2种是直接在本地安装PostgreSQL数据库并进行基本配置。

1.通过Docker容器来安装、部署

通过Docker容器安装、部署PostgreSQL数据库的命令如下:

执行命令后出现如图4-1所示的界面,说明已经成功启动PostgreSQL数据库。

说明:PostgreSQL服务在Docker中的容器名称是quarkus_test,PostgreSQL服务内部建立了一个名称为 quarkus_test 的数据库,用户名为 quarkus_test,密码为 quarkus_test,可从postgres:10.5 容器镜像中获取。内部和外部端口是一致的,都为 PostgreSQL 的标准端口5432。

图4-1 使用Docker容器启动PostgreSQL数据库

2.本地直接安装

首先要安装 PostgreSQL 数据库。下载 PostgreSQL 数据库安装文件并进行安装,关于PostgreSQL 数据库的安装步骤就不进行具体说明了。在 PostgreSQL 数据库安装完毕后,要做一些初始化配置。

首先,建立一个登录角色,用户名是 quarkus_test,密码也是 quarkus_test,如图 4-2所示。

图4-2 PostgreSQL管理界面的登录角色目录

其次,建立一个名为quarkus_test的数据库,如图4-3所示。

图4-3 PostgreSQL管理界面的数据库目录

这样就构建了一个基本的数据库开发环境。

4.1.2 案例简介

本案例介绍基于Quarkus 框架实现数据库操作基本功能。该模块以成熟的并且遵循JPA规范的Hibernate框架作为ORM的实现框架。通过阅读和分析在Hibernate框架上实现CRUD等操作(增加、检索、更新、删除等操作)的案例代码,可以理解和掌握 Quarkus 框架的ORM、JPA和Hibernate使用方法。

基础知识:ORM、JPA和Hibernate及其概念。

ORM(Object/Relation Mapping),即对象/关系映射。其核心思想是将关系型数据库表中的记录映射成对象,以对象的形式展现,开发者可以将对数据库的操作转化为对实体对象的操作。

JPA(Java Persistence API)表示JDK 5.0注解或XML描述ORM表的映射关系,并将运行期的实体对象持久化到数据库中。不过JPA只是一个接口规范。

Hibernate 是最流行的 ORM 框架,通过对象关系映射配置,可以完全脱离底层 SQL。同时,它也是通过JPA规范实现的一个轻量级框架。

4.1.3 编写程序代码

编写程序代码有 3种方式。第 1种方式是通过代码 UI来实现的,在 Quarkus 官网的生成代码页面中按照指定步骤生成脚手架代码,然后下载文件,将项目引入 IDE 工具中,最后修改程序源码。

第2种方式是通过mvn来构建程序,通过下面的命令创建Maven项目来实现:

第3种方式是直接从GitHub上获取代码,可以从GitHub上克隆预先准备好的示例代码:

该程序位于“031-quarkus-sample-orm-hibernate”目录中,是一个Maven工程项目程序。在IDE工具中导入Maven工程项目程序,在pom.xml的<dependencies>下有如下内容:

quarkus-hibernate-orm 是 Quarkus 扩展了 Hibernate 的 ORM 服务实现。quarkus-jdbc-postgresql是Quarkus 扩展了PostgreSQL的JDBC接口实现。

quarkus-sample-orm-hibernate 程序的应用架构(如图 4-4 所示)表明,外部访问ProjectResource资源接口,ProjectResource调用ProjectService服务,ProjectService服务调用注入的EntityManager对象并对PostgreSQL数据库执行对象持久化操作。ProjectService服务依赖于Hibernate框架和quarkus-jdbc扩展。

图4-4 quarkus-sample-orm-hibernate程序应用架构图

quarkus-sample-orm-hibernate程序的配置文件和核心类如表4-1所示。

表4-1 quarkus-sample-orm-hibernate程序的配置文件和核心类

在该程序中,首先看看配置信息的application.properties文件:

在application.properties文件中,配置了与数据库连接相关的参数。

(1)db-kind表示连接的数据库是PostgreSQL。

(2)quarkus.datasource.username和quarkus.datasource.password是用户名和密码,即PostgreSQL的登录角色名和密码。

(3)quarkus.datasource.jdbc.url定义了数据库的连接位置信息,其中jdbc:postgresql://localhost/quarkus_test中的quarkus_test是连接PostgreSQL的数据库。

(4)quarkus.hibernate-orm.database.generation=drop-and-create 表示程序启动后会重新创建表并初始化数据。

(5)quarkus.hibernate-orm.sql-load-script=import.sql 的含义是程序启动后会重新创建表并初始化数据需要调用的SQL文件。

下面让我们看看import.sql文件的内容:

import.sql主要实现了iiit_projects表的数据初始化工作。

下面讲解quarkus-sample-orm-hibernate程序中的ProjectResource资源类、ProjectService服务类和Project实体类的功能和作用。

1.ProjectResource资源类

用IDE工具打开com.iiit.quarkus.sample.orm.hibernate.ProjectResource类文件,其代码如下:

程序说明:

① ProjectResource 类主要用于与外部交互,其主要方法是 REST 的基本操作方法,包括GET、POST、PUT和DELETE方法。

②对后台的操作主要是通过注入的ProjectService对象来实现的。

2.ProjectService服务类

用IDE工具打开com.iiit.quarkus.sample.orm.hibernate.ProjectService类文件,其代码如下:

程序说明:

① ProjectService 类实现了 JPA 规范下的数据库操作,包括查询、新增、修改和删除等操作。

② ProjectService 类通过注入 EntityManager 对象,实现了后端数据库的 CRUD 操作。EntityManager对象是JPA规范的实体管理器。

③@Transactional注解是方法注解,表明该方法对数据库的操作具有事务性。

3.Project实体类

用IDE工具打开com.iiit.quarkus.sample.orm.hibernate.Project类文件,其代码如下:

程序说明:

①@Entity注解表示Project对象是一个遵循JPA规范的实体对象。

②@Table(name="iiit_projects")注解表示Project对象映射的关系型数据库表是iiit_projects。

③@NamedQuery(name="Projects.findAll",query="SELECT f FROM Project f ORDER BY f.name",hints=@QueryHint(name="org.hibernate.cacheable",value="true"))表示调用Projects.findAll方法时将使用后面的SQL查询语句。

④@Cacheable表明对象采用缓存模式。

该程序动态运行的序列图(如图 4-5 所示,遵循 UML 2.0 规范绘制)描述了外部调用者Actor、ProjectResource、ProjectService和EntityManager等4个对象之间的时间顺序交互关系。

图4-5 quarkus-sample-orm-hibernate程序动态运行的序列图

该序列图中总共有5个序列,分别介绍如下。

序列 1活动:① 外部调用 ProjectResource资源类的 GET(list)方法;② GET(list)方法调用ProjectService服务类的list方法;③ ProjectService服务类的list方法调用EntityManager的get方法;④返回整个Project列表。

序列2活动:① 外部传入参数ID并调用ProjectResource资源类的GET(getById)方法;② GET(getById)方法调用 ProjectService服务类的 getById方法;③ ProjectService服务类的 getById方法调用 EntityManager的 find方法;④ 返回 Project列表中对应 ID的 Project对象。

序列 3活动:① 外部传入参数 Project对象并调用 ProjectResource资源类的 POST(add)方法;② POST(add)方法调用 ProjectService服务类的 add方法;③ ProjectService服务类的 add方法调用 EntityManager的 persist方法;④ EntityManager的 persist方法实现增加一个 Project对象的操作并返回参数Project对象。

序列4活动:① 外部传入参数Project对象并调用ProjectResource资源类的PUT(update)方法;② PUT(update)方法调用 ProjectService服务类的 update方法;③ ProjectService服务类根据项目名称是否相等来实现修改一个Project对象的操作并调用EntityManager的persist方法;④EntityManager的persist方法实现并返回参数Project对象。

序列 5 活动:① 外部传入参数 Project 对象并调用 ProjectResource 资源类的DELETE(delete)方法;② DELETE(delete)方法调用 ProjectService服务类的 delete方法;③ ProjectService服务类根据项目名称是否相等来实现调用 EntityManager的 remove方法的操作;④EntityManager的remove方法实现删除一个Project对象的操作并返回。

4.1.4 验证程序

通过下列几个步骤(如图4-6所示)来验证案例程序。

图4-6 quarkus-sample-orm-hibernate程序验证流程图

下面对其中涉及的关键点进行说明。

1.启动PostgreSQL数据库

首先要启动 PostgreSQL数据库,然后可以进入 PostgreSQL的图形管理界面并观察数据库中数据的变化情况。

2.启动quarkus-sample-orm-hibernate程序服务

启动程序有两种方式,第 1种是在开发工具(如 Eclipse)中调用 ProjectMain类的 run方法,第2种是在程序目录下直接运行命令mvnw compile quarkus:dev。

3.通过API显示项目的JSON格式内容

在命令行窗口中键入命令curl http://localhost:8080/projects,将返回整个项目列表的项目数据。

4.通过API显示单条记录

在命令行窗口中键入命令curl http://localhost:8080/projects/1,将返回项目1的项目数据。

5.通过API增加一条数据

在命令行窗口中键入如下命令:

可采用命令curl http://localhost:8080/projects显示全部内容,观察是否成功增加了数据。6.通过API修改一条数据的内容

在命令行窗口中键入如下命令:

可采用命令curl http://localhost:8080/projects/5来查看数据的变化情况。

7.通过API删除project6记录

在命令行窗口中键入如下命令:

命令执行完成后,调用命令curl http://localhost:8080/projects显示该记录,查看变化情况。

4.1.5 其他数据库配置的实现

本案例采用的数据库是PostgreSQL数据库,事实上Quarkus 支持多种数据库。Quarkus 不但可以通过常用方法使用数据源并配置 JDBC驱动程序,还可以采用响应式驱动程序以响应式的方式连接到数据库。针对JDBC驱动程序,首选的数据源和连接池实现是Agroal。而对于响应式驱动,Quarkus 使用 Vert.x响应式驱动程序。Agroal和 Vert.x都可以通过统一、灵活的配置进行协同。

1.Quarkus 中首选的JDBC数据源和连接池实现Agroal

Agroal 是一个现代的、轻量级的连接池实现,可用于高性能和高可伸缩性场景,并可与Quarkus 中的其他组件(如安全性、事务管理、健康度量等组件)集成。数据源配置就是添加Agroal 扩展和 jdbc-db2、jdbc-derby、jdbc-h2、jdbc mariadb、jdbc mssql、jdbc mysql 或 jdbc postgresql 之一。由于默认使用了 Agroal 扩展,配置文件中只需添加数据源即可。配置信息如下:

例如,要配置的数据源是H2数据库,修改为如下内容即可:

2.Quarkus 支持的内置数据库类型

数据库类型配置会定义要连接到的数据库类型。Quarkus 目前支持的内置数据库类型有DB2:db2、Derby:derby、H2:h2、MariaDB:mariadb、Microsoft SQL Server:mssql、MySQL:mysql、PostgreSQL:postgresql、pgsql或pg等。在Quarkus 配置数据库类型时,可以直接使用数据源 JDBC驱动程序扩展并在配置中定义内置数据库类型,Quarkus 会自动解析 JDBC驱动程序。

如果使用的不是上面列出的内置数据库类型的数据库,可使用 other 选项并显式定义JDBC 驱动程序。Quarkus 应用程序在 JVM 模式下可支持任何 JDBC 驱动程序,但不支持将other的JDBC驱动程序编译为原生可执行程序。

在开发数据库程序时,很可能需要定义一些其他配置信息来访问数据库。这需要通过配置数据源的其他属性来实现,如用户名和密码等,相关代码如下:

Quarkus 还支持从Vault检索密码来配置数据源信息。

3.Quarkus 中JDBC的配置介绍

JDBC 是最常见的数据库连接模式。例如,在使用 Hibernate ORM 时,通常需要一个JDBC 数据源。这就需要将 quarkus agroal 依赖项添加到项目中,可以使用一个简单的 Maven命令进行添加:

Agroal是Hibernate ORM扩展的可传递依赖项。如果使用Hibernate ORM,则不需要显式地添加Agroal扩展依赖项,而只需要为关系型数据库驱动程序选择并添加Quarkus 扩展。

Quarkus 提供的驱动程序扩展有 DB2-jdbc-db2、Derby-jdbc-derby、H2-jdbc-h2、MariaDB-jdbc-mariadb、Microsoft SQL Server-jdbc-mssql、MySQL-jdbc-mysql、PostgreSQL-jdbc-postgresql 等。H2 和 Derby 数据库通常可以配置为以“嵌入式模式”运行。但需要注意,Quaruks扩展不支持将嵌入式数据库引擎编译为原生可执行程序。

使用内置数据源类型之一时,将自动解析 JDBC 驱动程序,它们的映射关系如表4-2所示。

表4-2 数据库类型到JDBC驱动程序的映射

如何处理没有内置扩展或使用其他驱动程序的数据库呢?如果需要(例如使用OpenTracing 驱动程序)或希望使用 Quarkus 没有内置 JDBC 驱动程序扩展的数据库,则可以使用特定的驱动程序。如果没有 Quarkus 的驱动扩展,虽然驱动程序可以在任何运行于 JVM模式下的 Quarkus 应用程序中正常工作,但是在将应用程序编译为原生可执行程序时,不会有效实现。若希望生成原生可执行程序,还是建议使用现有的Quarkus 扩展JDBC驱动程序。

下面是使用OpenTracing驱动程序的代码:

针对内置不支持的数据库访问(在JVM模式下数据库为Oracle),可采用如下定义:

如果需要在代码中直接访问数据源,则可以通过以下方式注入:

在上面的示例中,注入类型是 AgroalDataSource,这是 javax.sql.DataSource类型。因此,也可以直接注入javax.sql.DataSource。

4.常用的数据库类型配置方式

每个受支持的数据库都包含不同的JDBC URL配置选项,下面简单列出这些配置选项。(1)H2的配置方式

H2 是一个嵌入式数据库,它可以作为服务器运行,可以存储为文件,也可以完全驻留在内存中。

H2采用以下格式的连接URL:

例子:jdbc:h2:tcp://localhost/~/test,jdbc:h2:mem:myDB。

案例程序“032-quarkus-sample-orm-hibernate-h2”就是H2数据库,可详细了解。

(2)PostgreSQL的配置方式

PostgreSQL只作为服务器运行,下面的其他数据库也是这样。因此,必须指定连接的详细信息或使用默认值。PostgreSQL采用以下格式的连接URL:

不同部分的默认值如下:host默认是 localhost,port默认是 5432,database默认与用户名相同。

例子:jdbc:postgresql://localhost/test。

大部分案例程序都采用的是PostgreSQL。

(3)DB2的配置方式

DB2采用以下格式的连接URL:

例子:jdbc:db2://localhost:50000/MYDB:user=dbadm;password=dbadm。

(4)MySQL的配置方式

MySQL采用以下格式的连接URL:

例子:jdbc:mysql://localhost:3306/test。

(5)Microsoft SQL Server的配置方式

Microsoft SQL Server采用以下格式的连接URL:

例子:jdbc:sqlserver://localhost:1433;databaseName=AdventureWorks。

(6)Derby的配置方式

Derby 是一个嵌入式数据库,也可以作为服务器运行,该数据库可以存储为文件,也可以完全驻留在内存中。以下列出了所有相关选项。Derby采用以下格式的连接URL:

例子:jdbc:derby://localhost:1527/myDB,jdbc:derby:memory:myDB;create=true。

其他JDBC驱动程序与上述驱动程序的工作原理相同。

4.1.6 关于其他ORM实现

本案例采用的ORM是支持JPA规范的Hibernate。Quarkus 也支持其他的ORM,国内很多开发者采用 MyBatis作为 ORM框架。虽然现阶段 Quarkus 官方没有公布,但有一些开源爱好者已经在Quarkus 上实现了MyBatis扩展,感兴趣的读者可以上GitHub试用该扩展。