簡體   English   中英

如何在 JPA CriteriaQuery 的 ORDER BY 子句中重寫子查詢

[英]How to rewrite subquery in ORDER BY clause in JPA CriteriaQuery

我正在嘗試使用 CriteriaQuery 編寫 SQL 查詢,但我很難這樣做。 這個查詢基本上是獲取一個發貨清單,並按授權日期對它們進行排序。 此授權日期表示為狀態轉換消息表中第一條記錄的日期屬性,初始狀態為 3,最終狀態為 4。這是我的查詢:

SELECT s.id
FROM shipment s
ORDER BY (SELECT min(stm.date)
          FROM status_transition_message stm
          WHERE stm.initial_status = 1 AND stm.final_status = 3 AND stm.shipment_id = s.id) desc;

我嘗試了多種不同的解決方案,但到目前為止都沒有奏效。

我目前的迭代如下:

private void sortByAuthDate(Root<ShipmentTbl> root, CriteriaQuery<?> query, CriteriaBuilder builder, ListSort sort) {
        Subquery<Timestamp> authDateQuery = query.subquery(Timestamp.class);
        Root<StatusTransitionMessageTbl> stmRoot = authDateQuery.from(StatusTransitionMessageTbl.class);

        Predicate shipmentId = builder.equal(stmRoot.<ShipmentTbl>get("shipment").<String>get("id"), root.<String>get("id"));
        Predicate initialStatus = builder.equal(stmRoot.<Integer>get("initialStatus"), 3);
        Predicate finalStatus = builder.equal(stmRoot.<Integer>get("finalStatus"), 4);

        // returns the authorization date for each queried shipment
        authDateQuery.select(builder.least(stmRoot.<Timestamp>get("date")))
                .where(builder.and(shipmentId, initialStatus, finalStatus));

        Expression<Timestamp> authDate = authDateQuery.getSelection();
        Order o = sort.getSortDirection() == ListSort.SortDirection.ASC ? builder.asc(authDate) : builder.desc(authDate);

        query.multiselect(authDate).orderBy(o);
    }

這個方案的問題是CriteriaQuery生成的SQL查詢不支持ORDER BY子句中的子查詢,導致解析異常。

我的 CriteriaQuery-fu 不足以幫助您完成該部分,但您可以將 SQL 查詢重寫為:

SELECT s.id
FROM shipment s
LEFT JOIN status_transition_message stm 
ON stm.initial_status = 1 AND stm.final_status = 3 AND stm.shipment_id = s.id
GROUP BY s.id
ORDER BY min(stm.date) DESC;

對我來說,這似乎是比在ORDER BY子句中運行相關子查詢更快的解決方案,尤其是在具有不太復雜的優化器的 RDBMS 上。

所以我嘗試遵循@Lukas Eder 解決方案並達到了這個解決方案:

private void sortByAuthDate(Root<ShipmentTbl> root, CriteriaQuery<?> query, CriteriaBuilder builder, ShipmentListSort sort) {
        Join<ShipmentTbl, StatusTransitionMessageTbl> shipmentStatuses = root.join("shipmentStatus", JoinType.LEFT);

        Predicate initialStatus = builder.equal(shipmentStatuses.<Integer>get("initialStatus"), 1);
        Predicate finalStatus = builder.equal(shipmentStatuses.<Integer>get("finalStatus"), 3);

        Expression<Timestamp> authDate = builder.least(shipmentStatuses.<Timestamp>get("date"));
        Order o = sort.getSortDirection() == ShipmentListSort.SortDirection.ASC ? builder.asc(authDate) : builder.desc(authDate);

        shipmentStatuses.on(builder.and(initialStatus, finalStatus));

        query.multiselect(authDate).groupBy(root.<String>get("id")).orderBy(o);
    }
}

但現在它拋出這個異常:

ERROR o.h.e.jdbc.spi.SqlExceptionHelper - ERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list

發生這種情況是因為查詢稍后只會獲得不同的貨物,並且它要求排序列也出現在選擇中。 問題是我不知道如何強制 CriteriaQuery 將該列保留在 SELECT 語句中。 它會自動只放入 ORDER BY。

這是它在我的測試中執行的 JPQL 查詢:

select
        distinct generatedAlias0 
    from
        ShipmentTbl as generatedAlias0 
    left join
        generatedAlias0.shipmentStatus as generatedAlias1 with ( generatedAlias1.initialStatus=:param0 ) 
        and (
            generatedAlias1.finalStatus=:param1 
        ) 
    where
        lower(generatedAlias0.shipmentName) like :param2 
    group by
        generatedAlias0.id 
    order by
        min(generatedAlias1.date) desc

以及生成的 SQL 查詢:

select
            distinct shipmenttb0_.id as id1_13_,
            shipmenttb0_.archived_date as archived2_13_,
            shipmenttb0_.auth_code as auth_cod3_13_,
            shipmenttb0_.authorization_date as authoriz4_13_,
            shipmenttb0_.booked_in_by_user as booked_i5_13_,
            shipmenttb0_.business_channel as business6_13_,
            shipmenttb0_.courier as courier7_13_,
            shipmenttb0_.courier_amount as courier_8_13_,
            shipmenttb0_.courier_currency as courier_9_13_,
            shipmenttb0_.ship_to as ship_to39_13_,
            shipmenttb0_.estimated_shipment_date as estimat10_13_,
            shipmenttb0_.last_updated_date as last_up11_13_,
            shipmenttb0_.measurement_unit as measure12_13_,
            shipmenttb0_.original_submitted_date as origina13_13_,
            shipmenttb0_.packaging_type as packagi14_13_,
            shipmenttb0_.placeholder_message as placeho15_13_,
            shipmenttb0_.scheduled_period_of_day as schedul16_13_,
            shipmenttb0_.scheduled_shipment_date as schedul17_13_,
            shipmenttb0_.ship_from as ship_fr40_13_,
            shipmenttb0_.ship_origin as ship_or41_13_,
            shipmenttb0_.shipment_name as shipmen18_13_,
            shipmenttb0_.status as status19_13_,
            shipmenttb0_.submitted_date as submitt20_13_,
            shipmenttb0_.supplier_contact_email as supplie21_13_,
            shipmenttb0_.supplier_contact_name as supplie22_13_,
            shipmenttb0_.supplier_contact_phone_number as supplie23_13_,
            shipmenttb0_.supplier_email as supplie24_13_,
            shipmenttb0_.supplier_secondary_contact_email as supplie25_13_,
            shipmenttb0_.supplier_secondary_contact_name as supplie26_13_,
            shipmenttb0_.supplier_secondary_contact_phone_number as supplie27_13_,
            shipmenttb0_.tenant as tenant28_13_,
            shipmenttb0_.total_received_boxes as total_r29_13_,
            shipmenttb0_.total_units as total_u30_13_,
            shipmenttb0_.total_value as total_v31_13_,
            shipmenttb0_.total_volume as total_v32_13_,
            shipmenttb0_.total_weight as total_w33_13_,
            shipmenttb0_.tracking_number as trackin34_13_,
            shipmenttb0_.tt_note as tt_note35_13_,
            shipmenttb0_.tt_priority as tt_prio36_13_,
            shipmenttb0_.updated_by_user as updated37_13_,
            shipmenttb0_.weight_unit as weight_38_13_ 
        from
            shipment shipmenttb0_ 
        left outer join
            status_transition_message shipmentst1_ 
                on shipmenttb0_.id=shipmentst1_.shipment_id 
                and (
                    shipmentst1_.initial_status=? 
                    and shipmentst1_.final_status=?
                ) 
        where
            lower(shipmenttb0_.shipment_name) like ? 
        group by
            shipmenttb0_.id 
        order by
            min(shipmentst1_.date) desc limit ?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM