如何使用 fetch 或 fetchResults 正确获取 Page 作为 Querydsl 查询的结果?

How to get Page as result in Querydsl query with fetch or fetchResults properly?

您好,我想在这里实现的是,我想将可分页数据提交到 QueryDsl 查询中并获得结果作为页面,我该如何正确地做到这一点?这是我到目前为止所做的:

这是我的控制器:

@PostMapping("/view-latest-stock-by-product-codes")
public ResponseEntity<RequestResponseDTO<Page<StockAkhirResponseDto>>> findStockByProductCodes(
        @RequestBody StockViewByProductCodesDto request) {

    Page<StockAkhirResponseDto> stockAkhir = stockService.findByBulkProduct(request);

    return ResponseEntity.ok(new RequestResponseDTO<>(PESAN_TAMPIL_BERHASIL, stockAkhir));
}

在我的控制器中,我提交了如下所示的 StockViewByProductCodesDto:

@Data
public class StockViewByProductCodesDto implements Serializable {

    private static final long serialVersionUID = -2530161364843162467L;

    @Schema(description = "Kode gudang yang ingin di tampilkan", example = "GBKTJKT1", required = true)
    private String warehouseCode;

    @Schema(description = "id dari sebuah branch", example = "1", required = true)
    private Long branchId;

    @Schema(description = "Kode Branch", example = "JKT", required = true)
    private String branchCode;

    @Schema(description = "Kode Product yang merupakan kode yang di ambil dari master product", example = "[\"MCM-508\",\"TL-101\"]", required = true)
    private List<String> productCodes;

    @Schema(description = "Size of row per page", example = "15", required = true)
    @NotNull
    private int size;

    @Schema(description = "Page number", example = "1", required = true)
    @NotNull
    private int page;

    @Schema(description = "Sort by", example = "id", required = false)
    private String sort;
}

这是我的服务:

public Page<StockAkhirResponseDto> findByBulkProduct(StockViewByProductCodesDto request) {
    String warehouseCode = request.getWarehouseCode();
    Long branchId = request.getBranchId();
    String branchCode = request.getBranchCode();
    List<String> productCodes = request.getProductCodes();
    Set<String> productCodesSet = new HashSet<String>(productCodes);

    Pageable pageable = PageUtils.pageableUtils(request);

    Page<StockAkhirResponseDto> stockAkhir = iStockQdslRepository.findBulkStockAkhirPage(warehouseCode, branchId, branchCode, productCodesSet, pageable);

    return stockAkhir;
}

如您所见,我使用 PageUtils.pageableUtils(请求)提取可分页信息,这是我的 pageableUtils 函数,如下所示:

public static Pageable pageableUtils(RequestKeyword request) {
    int page = 0;
    int size = 20;

    if (request.getPage() > 0) {
        page = request.getPage() - 1;
    }

    if (request.getSize() > 0) {
        size = request.getSize();
    }

    if (!request.getSort().isEmpty()) {
        return PageRequest.of(page, size, Sort.by(request.getSort()).descending());
    } else {
        return PageRequest.of(page, size);
    }
    
}

获得 Pageable 数据后,我将其提交到我的存储库中,它看起来像这样:

public Page<StockAkhirResponseDto> findBulkStockAkhirPage(String warehouseCode, Long branchId, String branchCode,
                                                      Set<String> productCodes, Pageable pageable) {

    JPQLQuery<Tuple> query = new JPAQuery<>(em);

    long offset = pageable.getOffset();
    long limit = pageable.getPageSize();

    QStock qStock = QStock.stock;
    NumberExpression<Integer> totalQty = qStock.qty.sum().intValue();
    query = query.select(qStock.productId, qStock.productCode, totalQty).from(qStock)
            .where(qStock.warehouseCode.eq(warehouseCode), qStock.productCode.in(productCodes),
                    qStock.branchCode.eq(branchCode), qStock.branchId.eq(branchId))
            .groupBy(qStock.productId, qStock.productCode);

    query.limit(limit);
    query.offset(offset);
    QueryResults<Tuple> result = query.fetchResults();
    long total = result.getTotal();
    List<Tuple> rows = result.getResults();

    List<StockAkhirResponseDto> stockAkhirDto = rows.stream()
            .map(t -> new StockAkhirResponseDto(t.get(0, Long.class), t.get(1, String.class), t.get(2, Integer.class)))
            .collect(Collectors.toList());

    return new PageImpl<>(stockAkhirDto, pageable, total);
}

查看我的存储库时我的编辑器没有错误,我可以 运行 我的项目,但是当我执行我的存储库功能时,我得到了这个错误:

"org.hibernate.hql.internal.ast.QuerySyntaxException: expecting CLOSE, found ',' near line 1, column 38 [select count(distinct stock.productId, stock.productCode, stock.warehouseId, stock.warehouseCode, stock.branchCode, stock.branchId)\nfrom com.bit.microservices.b2b.warehouse.entity.Stock stock\nwhere stock.warehouseCode = ?1 and stock.productCode in ?2 and stock.branchCode = ?3 and stock.branchId = ?4]; nested exception is java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: expecting CLOSE, found ',' near line 1, column 38 [select count(distinct stock.productId, stock.productCode, stock.warehouseId, stock.warehouseCode, stock.branchCode, stock.branchId)\nfrom com.bit.microservices.b2b.warehouse.entity.Stock stock\nwhere stock.warehouseCode = ?1 and stock.productCode in ?2 and stock.branchCode = ?3 and stock.branchId = ?4]"

问题出在这里,在这一行:

QueryResults<Tuple> result = query.fetchResults();

当我执行该行时,它给了我那个错误,我尝试获取 fetchResult,因为我想获取总数的 .getTotal()。

但是如果我使用 .fetch() 执行查询,它工作正常,就像这样:

List<StockAkhirResponseDto> stockAkhirDto = query.fetch()

我的 sql 结果正确执行,我错过了什么?如何正确获取页面结果?

您的问题可能与打开的 QueryDSL issue 有关。记录的问题与 fetchCount 的使用有关,但我认为很可能也是您的情况。

考虑上述问题中的以下 comment

fetchCount() uses a COUNT function, which is an aggregate function. Your query already has aggregate functions. You cant aggregate aggregate functions, unless a subquery is used (which is not available in JPA). Therefore this use case cannot be supported.

该issue还提供了temporary solution.

基本上,想法是能够通过在初始 select 上创建语句来执行 COUNT。据我所知,QueryDsl 是不可能的,这就是为什么在指定的解决方法中他们访问 Hibernate 提供的下划线机制。

也许,您可以尝试避免限制的另一件事是为您的查询创建一个数据库视图,在其上创建相应的 QueryDsl 对象,并使用这些对象执行实际计算。我知道这不是一个理想的解决方案,但它会绕过当前的 QueryDsl 限制。