第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试用该扩展。