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

Quarkus Reactive+Hibernate Reactive触发Illegal pop()异常

问题分析与解决方案

核心原因

Illegal pop() with non-matching JdbcValuesSourceProcessingState异常本质是Hibernate Reactive会话上下文冲突导致的。Hibernate Reactive的Session并非线程安全,当你用Uni.combine().all()并行执行多个数据库查询时,多个操作会共享同一个上下文会话,导致会话内部的状态栈被非法修改。

修复方案

你需要确保每个数据库查询操作都运行在独立的会话上下文中,或者通过显式会话管理隔离并行操作。以下是两种可行的修复方式:

方式1:为每个查询显式绑定独立会话

用SessionFactory.withSession()包裹每个数据库操作,确保并行查询各自使用独立会话:

@Inject
SessionFactory sessionFactory;

@GET
@Path("/availabilities")
public Uni<List<VehicleAvailability>> vehicleAvailabilitiesForRide(@QueryParam("rideId") String rideId) {

    return sessionFactory.withSession(session -> 
        session.find(PlannedRide.class, rideId)
            .map(ride -> {
                if (ride == null) {
                    throw new NotFoundException();
                }
                return ride;
            })
    ).flatMap(ride -> {
        // 每个并行查询绑定独立会话
        Uni<List<VehicleReservation>> reservationsUni = sessionFactory.withSession(session -> 
            VehicleReservation.findReservationsInPeriod(ride.getStart(), ride.getEnd())
        );
        Uni<List<PlannedRide>> plannedRidesUni = sessionFactory.withSession(session -> 
            PlannedRide.findRidesInPeriod(ride.getStart(), ride.getEnd())
        );

        return Uni.combine().all().unis(reservationsUni, plannedRidesUni).asTuple();
    }).flatMap(tuple -> {
        PlannedRide ride = tuple.getItem1();
        List<VehicleReservation> reservations = tuple.getItem2();
        List<PlannedRide> plannedRides = tuple.getItem3();

        // 车辆查询同样使用独立会话
        return sessionFactory.withSession(session -> 
            session.createQuery("from Vehicle", Vehicle.class).list()
        ).map(vehicles -> vehicles.stream().map(vehicle -> {
            if (vehicle.isDefective()) {
                return new VehicleAvailability(vehicle, Availability.DEFECTIVE);
            }
            // 检查预约状态
            for (VehicleReservation reservation : reservations) {
                if (Objects.equals(reservation.getVehicle().getId(), vehicle.getId())) {
                    if (Objects.equals(reservation.getPlannedRide().getId(), ride.getId())) {
                        return new VehicleAvailability(vehicle, Availability.RESERVED);
                    } else {
                        return new VehicleAvailability(vehicle, Availability.NOT_AVAILABLE);
                    }
                }
            }
            // 检查重叠行程
            boolean hasOverlappingOtherRides = plannedRides.stream()
                .anyMatch(iterRide -> !Objects.equals(ride.getId(), iterRide.getId()));
            if (hasOverlappingOtherRides) {
                return new VehicleAvailability(vehicle, Availability.POTENTIALLY_AVAILABLE);
            }
            return new VehicleAvailability(vehicle, Availability.AVAILABLE);
        }).toList());
    });
}

方式2:调整执行顺序避免并行冲突

如果显式会话管理仍有问题,可以调整查询顺序,放弃部分并行性以避免上下文冲突:

@GET
@Path("/availabilities")
public Uni<List<VehicleAvailability>> vehicleAvailabilitiesForRide(@QueryParam("rideId") String rideId) {

    return PlannedRide.<PlannedRide>findById(rideId)
        .map(ride -> {
            if (ride == null) {
                throw new NotFoundException();
            }
            return ride;
        })
        // 先获取预约数据,再获取行程数据
        .flatMap(ride -> 
            VehicleReservation.findReservationsInPeriod(ride.getStart(), ride.getEnd())
                .map(reservations -> Tuple.of(ride, reservations))
        )
        .flatMap(tuple -> {
            PlannedRide ride = tuple.getItem1();
            List<VehicleReservation> reservations = tuple.getItem2();
            return PlannedRide.findRidesInPeriod(ride.getStart(), ride.getEnd())
                .map(plannedRides -> Tuple.of(ride, reservations, plannedRides));
        })
        .flatMap(tuple -> {
            PlannedRide ride = tuple.getItem1();
            List<VehicleReservation> reservations = tuple.getItem2();
            List<PlannedRide> plannedRides = tuple.getItem3();
            
            return Vehicle.findAll().list()
                .map(vehicles -> vehicles.stream().map(vehicle -> {
                    // 状态判断逻辑保持不变
                    if (vehicle.isDefective()) {
                        return new VehicleAvailability(vehicle, Availability.DEFECTIVE);
                    }
                    for (VehicleReservation reservation : reservations) {
                        if (Objects.equals(reservation.getVehicle().getId(), vehicle.getId())) {
                            if (Objects.equals(reservation.getPlannedRide().getId(), ride.getId())) {
                                return new VehicleAvailability(vehicle, Availability.RESERVED);
                            } else {
                                return new VehicleAvailability(vehicle, Availability.NOT_AVAILABLE);
                            }
                        }
                    }
                    boolean hasOverlappingOtherRides = plannedRides.stream()
                        .anyMatch(iterRide -> !Objects.equals(ride.getId(), iterRide.getId()));
                    if (hasOverlappingOtherRides) {
                        return new VehicleAvailability(vehicle, Availability.POTENTIALLY_AVAILABLE);
                    }
                    return new VehicleAvailability(vehicle, Availability.AVAILABLE);
                }).toList());
        });
}

额外注意事项

  • 检查findReservationsInPeriod和findRidesInPeriod的实现,确保它们依赖当前上下文会话执行查询(比如用Session.createQuery而非硬编码的静态会话)。
  • 避免在并行Uni操作中访问实体的懒加载属性,这会进一步加剧会话上下文冲突。

内容的提问来源于stack exchange,提问作者Patrick Lindner

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 15:16:08