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
相关产品推荐
相关产品推荐

