Quarkus 2.2测试Mutiny响应式事务代码报错:无法从IO线程启动JTA事务
问题原因分析
- 你的代码中出现了嵌套Uni结构
Uni<Uni<Integer>>,没有展开内层Uni是直接调用assertCompleted()收不到完成事件的核心原因:外层Uni触发完成时,内层的实际操作还未执行。你最后一步的Uni.createFrom().item(prestationRepository.deleteAll())写法错误,如果deleteAll本身已经返回Uni<Integer>,额外套Uni.createFrom().item()会直接把整个Uni作为外层Uni的元素,形成嵌套结构,不需要额外包裹。 - 默认JTA事务属于阻塞API,直接在IO线程调用就会抛出
BlockingOperationNotAllowedException,Quarkus针对响应式场景提供了专属的事务支持,不需要手动切换线程到阻塞执行器。
修复方案
第一步:引入响应式事务依赖(未引入时需要补充)
在pom.xml中添加Quarkus响应式事务扩展:
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-narayana-r2dbc</artifactId> </dependency>
如果使用Hibernate Reactive作为持久层,确保已经引入对应的响应式持久化扩展。
第二步:修改测试代码
给测试方法添加@TestReactiveTransaction注解处理测试场景的事务管理,同时调整代码消除Uni嵌套,不需要手动指定runSubscriptionOn,修改后代码如下:
@TestReactiveTransaction void myTest() { final Prestation prestation = Prestation.builder().build(); // 全程使用transformToUni避免Uni嵌套,最终得到Uni<Integer>结构 final Uni<Integer> result = Uni.createFrom().voidItem() .transformToUni(unused -> prestationCreate.upsert(prestation)) .transformToUni(aBoolean -> prestationClosedThePreviousDaySelect.select2Multi(LocalDate.of(2021, 04, 29)) .collect().asList() ) // 直接返回deleteAll的Uni,不要额外包Uni.createFrom().item() .transformToUni(prestations -> prestationRepository.deleteAll()); // 直接订阅最终的Uni等待结果即可 result.subscribe().withSubscriber(UniAssertSubscriber.create()) .awaitItem() .assertCompleted(); }
补充说明
如果你的业务方法(如upsert、deleteAll)需要事务支持,直接给对应方法加@ReactiveTransactional注解即可,该注解会自动绑定响应式场景的事务生命周期,不会阻塞IO线程,也不需要手动切换线程池。
如果需要在测试中手动控制事务边界,也可以用QuarkusTransaction的响应式API手动包裹逻辑:
Uni<Integer> result = QuarkusTransaction.requiringNew() .call(() -> { // 此处为事务内逻辑 return prestationCreate.upsert(prestation) .chain(() -> prestationClosedThePreviousDaySelect.select2Multi(LocalDate.of(2021, 04, 29)).collect().asList()) .chain(prestations -> prestationRepository.deleteAll()); });
内容的提问来源于stack exchange,提问作者Ben
相关产品推荐
相关产品推荐

