You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Quarkus 3.6.3链式调用Panache数据库操作测试报错求助

问题

从Spring切换到Quarkus,参考《Full Stack Quarkus and React》一书适配示例代码到Quarkus 3.6.3版本时,在第5章的后端测试中遇到问题。

原测试代码:

@Test
@TestSecurity(user = "user", roles = "user")
void updateForbidden() {
  final User admin = User.<User>findById(0L).await().indefinitely();
  Task adminTask = new Task();
  adminTask.title = "admins-task";
  adminTask.user = admin;
  adminTask = adminTask.<Task>persistAndFlush().await().indefinitely();
  given()
    .body("{\"title\":\"to-update\"}")
    .contentType(ContentType.JSON)
    .when().put("/api/v1/tasks/" + adminTask.id)
    .then()
    .statusCode(401); // TODO: TaskService UnauthorizedException should be changed to ForbiddenException
}

运行时出现错误:

java.lang.IllegalStateException: No current Vertx context found

    at io.quarkus.hibernate.reactive.panache.common.runtime.SessionOperations.vertxContext(SessionOperations.java:191)
    at io.quarkus.hibernate.reactive.panache.common.runtime.SessionOperations.getSession(SessionOperations.java:141)
    at io.quarkus.hibernate.reactive.panache.common.runtime.AbstractJpaOperations.getSession(AbstractJpaOperations.java:364)
    at io.quarkus.hibernate.reactive.panache.common.runtime.AbstractJpaOperations.findById(AbstractJpaOperations.java:98)

尝试添加@TestReactiveTransaction并使用UniAsserter后,代码变为:

@Test
@TestSecurity(user = "user", roles = "user")
@TestReactiveTransaction
void updateForbidden(UniAsserter asserter) {
    Task adminTask = new Task();
    asserter.execute(() -> {
        User.findById(0L).subscribe().with(admin -> {
            System.out.println("User found: " + ((User) admin).name);
            adminTask.title = "admins-task";
            adminTask.user = (User) admin;
            adminTask.<Task>persistAndFlush().subscribe().with(updatedAdminTask -> {
                System.out.println("UpdatedAdminTask: " + updatedAdminTask);
            }, throwable -> {
                System.err.println("There is something wrong ...");
                throwable.printStackTrace();
            });
        });
    });
}

但运行后出现会话关闭错误:

2023-12-21 17:09:37,834 DEBUG [org.hib.sql.res.int.ResultsHelper] (vert.x-eventloop-thread-1) Collection fully initialized: [test.user.User.roles#0]
User found: admin
2023-12-21 17:09:44,448 DEBUG [org.hib.SQL] (vert.x-eventloop-thread-1) select nextval('tasks_id_seq')
2023-12-21 17:10:06,185 FINE  [org.jun.jup.eng.exe.InvocationInterceptorChain$ValidatingInvocation] (main) The invocation is skipped
2023-12-21 17:10:06,190 DEBUG [org.hib.rea.eve.imp.AbstractReactiveSaveEventListener] (vert.x-eventloop-thread-1) Generated identifier: 4, using strategy: org.hibernate.reactive.id.impl.ReactiveGeneratorWrapper
2023-12-21 17:10:06,199 DEBUG [io.qua.run.Application] (main) Stopping application
2023-12-21 17:10:06,199 DEBUG [io.qua.run.shu.ShutdownRecorder] (main) Attempting to gracefully shutdown.
2023-12-21 17:10:06,215 DEBUG [org.hib.eng.tra.int.TransactionImpl] (vert.x-eventloop-thread-1) TransactionImpl created on closed Session/EntityManager
2023-12-21 17:10:06,215 DEBUG [org.hib.eng.tra.int.TransactionImpl] (vert.x-eventloop-thread-1) On TransactionImpl creation, JpaCompliance#isJpaTransactionComplianceEnabled == false
There is something wrong ...
org.hibernate.HibernateException: java.util.concurrent.CompletionException: java.lang.IllegalStateException: Session/EntityManager is closed
    at org.hibernate.reactive.session.impl.ReactiveExceptionConverter.convert(ReactiveExceptionConverter.java:28)
    at org.hibernate.reactive.session.impl.ReactiveSessionImpl.lambda$firePersist$11(ReactiveSessionImpl.java:741)
    ...

需要解决如何链式调用两次Panache数据库操作并修复上述报错。

解决方案

核心原因

使用subscribe()会异步执行操作,但测试主线程不会等待异步任务完成就结束,导致@TestReactiveTransaction管理的事务提前关闭,会话被销毁,后续数据库操作报错。

正确实现方式

利用Quarkus提供的UniAsserter配合Uni的链式调用(flatMap()),让测试框架正确等待所有异步操作完成,同时保持事务上下文有效。

完整的修复后代码:

@Test
@TestSecurity(user = "user", roles = "user")
@TestReactiveTransaction
void updateForbidden(UniAsserter asserter) {
    asserter.execute(() -> 
        // 链式调用:先查询admin用户,再创建并保存Task
        User.<User>findById(0L)
            .flatMap(admin -> {
                Task adminTask = new Task();
                adminTask.title = "admins-task";
                adminTask.user = admin;
                return adminTask.<Task>persistAndFlush();
            })
            // 保存完成后执行REST请求并验证状态码
            .flatMap(savedTask -> 
                given()
                    .body("{\"title\":\"to-update\"}")
                    .contentType(ContentType.JSON)
                    .when().put("/api/v1/tasks/" + savedTask.id)
                    .then()
                    .statusCode(403) // 按原代码TODO提示改为403 Forbidden
                    .extract().response()
                    .toCompletionStage()
            )
    );
}

关键说明

  • 避免subscribe():改用flatMap()实现异步操作的链式调用,让UniAsserter能跟踪并等待整个异步流完成。
  • 事务上下文保持:@TestReactiveTransaction会在整个异步流执行期间维持有效的会话和事务,不会提前关闭。
  • 状态码修正:按照原代码中的TODO提示,将状态码从401改为403(用户已认证但无权限,属于Forbidden场景)。

内容的提问来源于stack exchange,提问作者Thorsten Kitz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 11:54:52