Your second Quarkus application

本教程向你展示如何创建一个向数据库中写入和读取数据的应用程序。你将使用 Dev Services,因此你实际上不必自己下载、配置,甚至启动数据库。你还可以使用 Panache,即 Hibernate ORM 之上的一层,以 упростить чтение和写入数据。 本指南可以帮助你:

  • 向数据库读取和写入对象

  • 针对服务进行开发和测试,无需配置

Prerequisites

如要完成本指南,您需要:

  • Roughly 15 minutes

  • An IDE

  • 安装了 JDK 17+,已正确配置 JAVA_HOME

  • Apache Maven ${proposed-maven-version}

  • 如果你想使用 Quarkus CLI, 则可以选择使用

  • 如果你想构建一个本机可执行文件(或如果你使用本机容器构建,则使用 Docker),则可以选择安装 Mandrel 或 GraalVM 以及 configured appropriately

本教程基于你编写 your first Quarkus application 时学到的知识。你不需要该应用程序的代码,但务必理解这些概念。

Solution

我们建议你按照 Bootstrapping the project 及后续章节中的说明逐步创建应用程序。

但是,你可以直接转到已完成的示例。

下载 $${quickstarts-base-url}/archive/main.zip[归档] 或克隆 git 存储库:

git clone {quickstarts-clone-url}

解决方案位于 getting-started-dev-services $${quickstarts-base-url}/tree/main/getting-started-dev-services[目录] 中。

Outline steps

  • Bootstrap the application

  • 更新应用程序以读取用户输入

  • Create a Panache Entity

  • 读取和写入实体

  • 使用配置文件配置外部数据库

Setting up an interactive application

Bootstrapping the project

创建新 Quarkus 项目的最简单方法是打开一个终端并运行以下命令:

CLI
quarkus create app {create-app-group-id}:{create-app-artifact-id} \
    --no-code
cd {create-app-artifact-id}

要创建一个 Gradle 项目,添加 --gradle--gradle-kotlin-dsl 选项。 有关如何安装和使用 Quarkus CLI 的详细信息,请参见 Quarkus CLI 指南。

Maven
mvn {quarkus-platform-groupid}:quarkus-maven-plugin:{quarkus-version}:create \
    -DprojectGroupId={create-app-group-id} \
    -DprojectArtifactId={create-app-artifact-id} \
    -DnoCode
cd {create-app-artifact-id}

要创建一个 Gradle 项目,添加 -DbuildTool=gradle-DbuildTool=gradle-kotlin-dsl 选项。

适用于 Windows 用户:

  • 如果使用 cmd,(不要使用反斜杠 \ ,并将所有内容放在同一行上)

  • 如果使用 Powershell,将 -D 参数用双引号引起来,例如 "-DprojectArtifactId={create-app-artifact-id}"

有关在生成的应用程序中的内容的说明,请参阅 First application guide.

Running the application

在 dev 模式下启动应用程序

CLI
quarkus dev
Maven
./mvnw quarkus:dev
Gradle
./gradlew --console=plain quarkusDev

应用程序启动后,访问 [role="bare"][role="bare"]http://localhost:8080/hello. 它应该显示消息“Hello from Quarkus REST”。

Accepting user input

让我们使应用程序更具交互性。在 IDE 中打开项目,然后导航到 src/main/java/org/acme/GreetingResource.java.在 hello 方法中添加一个查询参数。(org.jboss.resteasy.reactive.RestQuery 注释就像 Jakarta REST `@QueryParam`注释,只是您不需要重复参数名称。)

public String hello(@RestQuery String name) {
    return "Hello " + name;
}

访问 [role="bare"][role="bare"]http://localhost:8080/hello?name=Bloom.

您应该看到一条个性化消息:Hello Bloom.

Fixing the tests

在您的 Quarkus 终端中,键入“r”来运行测试。您应该看到您的应用程序更改中断了测试!

若要修复测试,请打开 `src/test/java/org/acme/GreetingResourceTest.java`并替换

             .body(is("Hello from Quarkus REST"));

带有

             .body(containsString("Hello"));

这仍然验证了 HTTP 端点,但它对预期的输出更灵活。您应该在终端中看到测试现在通过了。

Adding persistence

Creating a Panache Entity

  1. 要添加持久性库,请运行

CLI
quarkus extension add {add-extension-extensions}
Maven
./mvnw quarkus:add-extension -Dextensions='{add-extension-extensions}'
Gradle
./gradlew addExtension --extensions='{add-extension-extensions}'

应用程序将记录它问候的人的姓名。通过创建一个 Greeting.java 类来定义一个实体。添加以下内容:

import io.quarkus.hibernate.orm.panache.PanacheEntity;
import jakarta.persistence.Entity;

@Entity
public class Greeting extends PanacheEntity {
    public String name;
}

实体使用 Panache,它是 Hibernate ORM 上的一层。扩展 PanacheEntity 引入了用于读取、写入和查找数据的一系列方法。因为所有数据访问方法都在 Greeting 实体上,而不是在单独的数据访问类上,所以这是活动记录模式的示例。

Greeting 表将有一列,一个名为 name 的字段。

Writing data

要使用新的实体,请更新 hello 方法以开始写入一些数据。

将该方法更改为以下内容:

@GET
@Transactional
@Produces(MediaType.TEXT_PLAIN)
public String hello(@QueryParam("name") String name) {
   Greeting greeting = new Greeting();
   greeting.name = name;
   greeting.persist();
   return "Hello " + name;
}

别忘记 @Transactional 注释,它确保写入包装在一个事务中。

GETs should not change application state.

通常,你不应该在 GET REST 方法中进行状态更新,但这里可以让尝试变得更简单。我们假设正在编写的是一个日志“副作用”,而不是一个有意义的状态更改!

通过访问 [role="bare"][role="bare"]http://localhost:8080/hello?name=Bloom 尝试更新的端点。你应该看见“Hello Bloom”消息。

Reading data

尽管新持久性代码看起来运行时没有任何错误,但你如何知道正在向数据库写入任何内容?

GreetingResource 添加第二个 REST 方法。

@GET
@Path("names")
@Produces(MediaType.TEXT_PLAIN)
public String names() {
    List<Greeting> greetings = Greeting.listAll();
    String names = greetings.stream().map(g-> g.name)
       .collect(Collectors.joining (", "));
    return "I've said hello to " + names;
}

要试用它,请访问 [role="bare"][role="bare"]http://localhost:8080/hello?name=Bloom,然后 [role="bare"][role="bare"]http://localhost:8080/hello/names。

你应该会看到以下信息:“我已向 Bloom 说你好”。

Example 1. a container runtime is required.

请务必记住,你需要备有可用的容器运行时,否则此时你将开始在 Quarkus 日志中看到错误。

Dev services

读写数据库似乎工作正常,但这是有点意外的。PostgreSQL 数据库哪来的?你没有设置任何东西。

该数据库正在使用 Dev Services 进行管理。Dev Services 会负责停止和启动你的应用程序所需的各种服务。由于你包含了 jdbc-postgresql 依赖关系,所以数据库将是容器化的 PostgreSQL 数据库。如果你改用 jdbc-mysql,你的数据库将成为容器化的 MySQL 数据库。

如果你愿意,可以使用你的容器工具查看正在运行的容器。例如,如果你正在使用 Docker,请运行 docker ps;如果你正在使用 podman,请运行 podman ps。你应该会看到类似以下内容的输出:

ff88dcedd899  docker.io/library/postgres:14  postgres -c fsync...  20 minutes ago  Up 20 minutes  0.0.0.0:34789->5432/tcp  nostalgic_bassi

停止 Quarkus 并再次运行 docker ps。你应该看不到任何正在运行的内容(容器关闭可能需要几分钟时间)。Quarkus 应用程序停止后,该容器会自动停止。

Initialising services

如果你对你的代码进行更多调整,你可能会注意到,有时在进行应用程序更改之后,[role="bare"][role="bare"]http://localhost:8080/hello/names 不会列出任何名称。出了什么事?默认情况下,在 dev 模式下,使用 Dev Services 数据库时,Quarkus 会将 Hibernate ORM 数据库生成配置为 drop-and-create。有关更多详情,请参阅 Hibernate configuration reference。如果某个代码更改触发了应用程序重新启动,则数据库表将被删除并重新创建。

这很方便,但是如果你更希望数据库始终包含内容,那该怎么办呢?这样会使测试变得更容易。如果你提供了一个 import.sql 文件,Quarkus 会使用它在每次启动时对数据库进行初始化。

  1. 在你的项目中创建一个 src/main/resources/import.sql 文件

  2. 添加以下 SQL 语句:

INSERT INTO Greeting(id, name)
VALUES (nextval('Greeting_SEQ'), 'Alice');
INSERT INTO Greeting(id, name)
VALUES (nextval('Greeting_SEQ'), 'Bob');

然后在你的 dev 模式会话中点击 s 以强制进行完整重启。然后访问 [role="bare"][role="bare"]http://localhost:8080/hello/names。

你会看到 Alice 和 Bob 始终包含在名称列表中。

Controlling Dev Services

Using an external database instead

如果你更愿意使用由你自己管理的外部数据库,该怎么办呢?将以下内容添加到 src/main/resources/application.properties

# configure your datasource
quarkus.datasource.db-kind = postgresql
quarkus.datasource.username = leopold
quarkus.datasource.password = bloom
quarkus.datasource.jdbc.url = jdbc:postgresql://localhost:5432/mydatabase

这会告诉 Quarkus 不启用 Dev Service,因为你拥有自己的数据库。你不必担心启动数据库,因为你只是想看看如何更改配置。

访问 [role="bare"][role="bare"]http://localhost:8080/hello/names. 您将收到红色错误屏幕,而不是名称列表。在运行 Quarkus 的终端中,您将看到以下堆栈错误消息:

2023-06-28 19:18:22,880 ERROR [io.qua.ver.htt.run.QuarkusErrorHandler] (executor-thread-1) HTTP Request to /hello?name=fred failed, error id: 4f9b5ce6-3b08-41c5-af36-24eee4d1dd2b-2: org.hibernate.exception.JDBCConnectionException: Unable to acquire JDBC Connection [Connection to localhost:5432 refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections.] [n/a]
        at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:98)
        at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:56)
...

这十分合理;您已禁用数据库开发服务,但尚未启动您自己的数据库。

Using profiles

除非您想要这样做,否则不必担心设置一个外部数据库来解决此连接错误。相反,您可以返回使用开发服务。它让生活变得更加容易!

但生产呢?您不会希望在生产中使用开发服务。事实上,Quarkus 仅在开发和测试模式中启动开发服务。

配置一个外部数据库并让它 only 在生产中使用,同时您也可以在其余时间使用开发服务,这难道不是很好吗?

在数据库配置中添加一个 %prod. 前缀。这意味着该配置仅适用于 prod profile

该配置应如下所示:

# configure your datasource
%prod.quarkus.datasource.db-kind = postgresql
%prod.quarkus.datasource.username = leopold
%prod.quarkus.datasource.password = bloom
%prod.quarkus.datasource.jdbc.url = jdbc:postgresql://localhost:5432/mydatabase

现在外部数据库将在生产模式中使用,而开发服务将在开发和测试模式中使用。

检查 [role="bare"][role="bare"]http://localhost:8080/hello/names. 它应该可以再次工作了,因为开发服务已被重新启用。请注意,对于所有这些更改,无需重新启动 Quarkus。

Summary

您已经将一个简单的 REST 应用程序更新为使用 Hibernate ORM 和 Panache 编写和读取数据库中的数据。数据已持久到一个“真实”的数据库,而您无需配置任何内容。