1. 概述
在大规模高性能系统中构建**反应式应用已成为Java开发的核心需求。Hibernate Reactive和Quarkus是两大强力工具,能帮助开发者高效构建反应式应用。Hibernate Reactive是Hibernate ORM的反应式扩展,专为无缝对接非阻塞数据库驱动而设计**。
另一方面,Quarkus是Kubernetes原生Java框架,针对GraalVM和OpenJDK HotSpot优化,专门用于构建反应式应用。两者结合为构建高性能、可扩展的反应式Java应用提供了强大平台。
本教程将通过从零构建一个反应式银行存款系统,深入探讨Hibernate Reactive和Quarkus。同时,我们将集成测试以确保应用的正确性和可靠性。
2. Quarkus中的反应式编程
Quarkus作为知名反应式框架,从诞生之初就将反应性作为架构核心要素。该框架集成了丰富的反应式特性,并拥有强大的生态系统支撑。
值得注意的是,Quarkus通过Mutiny提供的Uni和Multi类型实现反应式概念,充分体现了对异步和事件驱动编程范式的坚定承诺。
3. Quarkus中的Mutiny
Mutiny是Quarkus中处理反应式特性的核心API。大多数扩展通过返回Uni和Multi的API支持Mutiny,它们通过非阻塞背压处理异步数据流。
我们的应用通过Quarkus提供的Uni和Multi类型利用反应式概念:
- Multi:表示可异步发射多个项目的类型,类似java.util.stream.Stream但具备背压处理能力。当处理潜在无界数据流时使用Multi,例如实时流式传输多笔银行存款。
- Uni:表示最多发射一个项目或错误的类型,类似java.util.concurrent.CompletableFuture但拥有更强大的组合操作符。Uni用于预期单个结果或错误的场景,例如从数据库查询单笔银行存款。
4. 理解PanacheEntity
当在Quarkus中使用Hibernate Reactive时,**PanacheEntity类提供了一种定义JPA实体精简方案,极大减少样板代码*。通过继承Hibernate的PanacheEntityBase,PanacheEntity*获得反应式能力,使实体能以非阻塞方式管理。
这允许高效处理数据库操作而不阻塞应用执行,从而提升整体性能。
5. Maven依赖
首先在pom.xml中添加*quarkus-hibernate-reactive-panache*依赖:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-reactive-panache</artifactId>
<version>3.11.0</version>
</dependency>
配置好依赖后,我们就可以在示例实现中使用它了。
6. 真实世界示例代码
银行系统通常面临高并发需求。我们可以利用Quarkus、Hibernate和反应式编程等技术实现关键服务。
本示例重点实现两个核心服务:创建银行存款和流式传输所有存款记录。
6.1 创建银行存款实体
ORM(对象关系映射)实体是所有CRUD系统的基础。该实体实现数据库对象到软件对象模型的映射,便于数据操作。正确定义Deposit实体对系统稳定运行和数据管理至关重要:
@Entity
public class Deposit extends PanacheEntity {
public String depositCod;
public String currency;
public String amount;
// 标准setter和getter方法
}
本例中,Deposit类继承PanacheEntity类,使其成为由Hibernate Reactive管理的反应式实体。
通过继承,Deposit类自动获得CRUD(创建、读取、更新、删除)操作方法和查询能力,显著减少手动编写SQL或JPQL的需求。这种方式简化了数据库操作管理,提升了系统整体效率。
6.2 实现Repository
虽然通常可直接使用存款实体进行CRUD操作,但本例中我们创建了专门的DepositRepository:
@ApplicationScoped
public class DepositRepository {
@Inject
Mutiny.SessionFactory sessionFactory;
@Inject
JDBCPool client;
public Uni<Deposit> save(Deposit deposit) {
return sessionFactory.withTransaction((session, transaction) -> session.persist(deposit)
.replaceWith(deposit));
}
public Multi<Deposit> streamAll() {
return client.query("SELECT depositCode, currency,amount FROM Deposit ")
.execute()
.onItem()
.transformToMulti(set -> Multi.createFrom()
.iterable(set))
.onItem()
.transform(Deposit::from);
}
}
该Repository实现了自定义*save()方法和streamAll()方法,允许我们以Multi
6.3 实现REST接口
现在通过REST接口暴露我们的反应式方法:
@Path("/deposits")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class DepositResource {
@Inject
DepositRepository repository;
@GET
public Uni<Response> getAllDeposits() {
return Deposit.listAll()
.map(deposits -> Response.ok(deposits)
.build());
}
@POST
public Uni<Response> createDeposit(Deposit deposit) {
return deposit.persistAndFlush()
.map(v -> Response.status(Response.Status.CREATED)
.build());
}
@GET
@Path("stream")
public Multi<Deposit> streamDeposits() {
return repository.streamAll();
}
}
如代码所示,该REST服务包含三个反应式方法:
- getAllDeposits():返回所有存款,类型为Uni
- createDeposit():创建存款,类型为Uni
- streamDeposits():流式传输存款,类型为Multi
7. 测试
为确保应用的准确性和可靠性,我们将使用JUnit和*@QuarkusTest*编写集成测试。通过验证软件各组件的功能性和性能,早期发现并修复问题,构建更健壮可靠的应用:
@QuarkusTest
public class DepositResourceIntegrationTest {
@Inject
DepositRepository repository;
@Test
public void givenAccountWithDeposits_whenGetDeposits_thenReturnAllDeposits() {
given().when()
.get("/deposits")
.then()
.statusCode(200);
}
}
该测试仅验证REST接口连接和存款创建功能。但需注意,并非所有测试都如此简单。在*@QuarkusTest*中测试反应式Panache实体比测试普通Panache实体更复杂。
复杂性的根源在于API的异步特性,以及所有操作必须在Vert.x事件循环上执行的强制要求。
首先,我们在pom.xml中添加测试范围的*quarkus-test-hibernate-reactive-panache*依赖:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-test-hibernate-reactive-panache</artifactId>
<version>3.3.3</version>
<scope>test</scope>
</dependency>
集成测试方法需标注*@RunOnVertxContext*,使其在Vert.x线程而非主线程上运行。
此注解对测试必须在事件循环上执行的组件特别有用,能更真实地模拟实际运行环境。
TransactionalUniAsserter是单元测试方法的预期输入类型。它像拦截器一样工作,将每个断言方法包装在独立的反应式事务中。这能更精确地管理测试环境,确保每个断言方法在隔离上下文中运行:
@Test
@RunOnVertxContext
public void givenDeposit_whenSaveDepositCalled_ThenCheckCount(TransactionalUniAsserter asserter){
asserter.execute(() -> repository.save(new Deposit("DEP20230201","10","USD")));
asserter.assertEquals(() -> Deposit.count(), 2l);
}
接下来为返回*Multi
@Test
public void givenDepositsInDatabase_whenStreamAllDeposits_thenDepositsAreStreamedWithDelay() {
Deposit deposit1 = new Deposit("67890", "USD", "200.0");
Deposit deposit2 = new Deposit("67891", "USD", "300.0");
repository.save(deposit1)
.await()
.indefinitely();
repository.save(deposit2)
.await()
.indefinitely();
Response response = RestAssured.get("/deposits/stream")
.then()
.extract()
.response();
// Then: 响应包含带延迟的流式传输存款记录
response.then()
.statusCode(200);
response.then()
.body("$", hasSize(2));
response.then()
.body("[0].depositCode", equalTo("67890"));
response.then()
.body("[1].depositCode", equalTo("67891"));
}
该测试旨在验证通过*Multi
8. 结论
本文探讨了使用Hibernate Reactive和Quarkus进行反应式编程的核心概念。我们介绍了Uni和Multi的基础知识,并通过集成测试验证代码正确性。
使用Hibernate Reactive和Quarkus进行反应式编程,可实现高效的非阻塞数据库操作,使应用响应更迅速、扩展性更强。借助这些工具,我们能构建满足现代高性能环境需求的云原生应用。
本教程的完整源代码可在GitHub获取。