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

如何在响应式Quarkus应用中处理分页并解决HR000068错误?

Quarkus响应式分页实现方案(解决HR000068错误)

错误原因分析

HR000068错误本质是Hibernate Reactive的操作必须严格在Vert.x EventLoop线程执行,你合并两个Uni时,大概率是其中某个操作意外切换到了非EventLoop线程(比如误用阻塞操作、手动指定了Worker线程调度),导致会话上下文失效。


核心解决方案

1. 优先利用Panache Reactive原生分页能力

Panache Reactive已经封装了分页和计数的便捷API,直接使用即可避免手动会话管理的坑:

步骤1:定义分页响应体

public class PaginatedResponse<T> {
    private List<T> data;
    private long totalElements;
    private int currentPage;
    private int pageSize;

    public PaginatedResponse(List<T> data, long totalElements, int currentPage, int pageSize) {
        this.data = data;
        this.totalElements = totalElements;
        this.currentPage = currentPage;
        this.pageSize = pageSize;
    }

    // 省略getter/setter
}

步骤2:Reactive Routes中实现分页接口

import io.quarkus.hibernate.reactive.panache.PanacheQuery;
import io.quarkus.hibernate.reactive.panache.common.Page;
import io.smallrye.mutiny.Uni;
import io.vertx.ext.web.RoutingContext;
import io.vertx.core.json.Json;

public class EntityResource {

    public void handlePaginatedRequest(RoutingContext ctx) {
        // 从请求参数获取分页参数
        int page = Integer.parseInt(ctx.request().getParam("page", "0"));
        int size = Integer.parseInt(ctx.request().getParam("size", "10"));

        // 1. 构建分页查询
        PanacheQuery<YourEntity> query = YourEntity.findAll().page(Page.of(page, size));
        // 2. 并行获取数据列表和总条数(均在EventLoop线程执行)
        Uni<List<YourEntity>> listUni = query.list();
        Uni<Long> countUni = YourEntity.count();

        // 3. 合并两个Uni并映射成分页响应
        Uni.combine().all().unis(listUni, countUni)
                .asTuple()
                .map(tuple -> new PaginatedResponse<>(
                        tuple.getItem1(),
                        tuple.getItem2(),
                        page,
                        size
                ))
                .subscribe().with(
                        response -> ctx.response()
                                .putHeader("Content-Type", "application/json")
                                .end(Json.encode(response)),
                        error -> ctx.fail(error)
                );
    }
}

2. 手动管理会话(复杂场景下)

如果需要自定义查询逻辑,必须确保所有Hibernate操作在同一个会话且EventLoop线程执行,可通过@WithSession注解或Panache.withSession方法实现:

方式1:使用@WithSession注解

import io.quarkus.hibernate.reactive.panache.common.WithSession;
import io.smallrye.mutiny.Uni;

public class CustomEntityResource {

    @WithSession // 确保方法内的Hibernate操作在EventLoop线程的会话中执行
    public Uni<PaginatedResponse<YourEntity>> getCustomPaginatedData(int page, int size) {
        PanacheQuery<YourEntity> query = YourEntity.find("status = ?1", "ACTIVE")
                .page(Page.of(page, size));
        
        return Uni.combine().all().unis(query.list(), query.count())
                .asTuple()
                .map(tuple -> new PaginatedResponse<>(
                        tuple.getItem1(),
                        tuple.getItem2(),
                        page,
                        size
                ));
    }
}

方式2:手动绑定会话

import io.quarkus.hibernate.reactive.panache.Panache;
import io.smallrye.mutiny.Uni;

public class CustomEntityResource {

    public Uni<PaginatedResponse<YourEntity>> getCustomPaginatedData(int page, int size) {
        return Panache.withSession(session -> {
            // 在会话内执行所有查询
            Uni<List<YourEntity>> listUni = session.createQuery("FROM YourEntity WHERE status = :status", YourEntity.class)
                    .setParameter("status", "ACTIVE")
                    .setFirstResult(page * size)
                    .setMaxResults(size)
                    .getResultList();
            
            Uni<Long> countUni = session.createQuery("SELECT COUNT(*) FROM YourEntity WHERE status = :status", Long.class)
                    .setParameter("status", "ACTIVE")
                    .getSingleResult();
            
            return Uni.combine().all().unis(listUni, countUni)
                    .asTuple()
                    .map(tuple -> new PaginatedResponse<>(
                            tuple.getItem1(),
                            tuple.getItem2(),
                            page,
                            size
                    ));
        });
    }
}

关键注意事项

  • 绝对禁止阻塞操作:不要在Uni的回调中使用block()、await()等方法,会强制切换到非EventLoop线程,触发错误。
  • 避免手动线程调度:不要随意使用runSubscriptionOn()或emitOn()指定Worker线程,Hibernate Reactive操作必须在EventLoop线程执行。
  • 复用同一会话:复杂查询时,确保列表查询和计数查询在同一个会话内完成,避免会话上下文丢失。

内容的提问来源于stack exchange,提问作者Sergio Rodríguez Calvo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 19:16:19