简体   繁体   English

如何在 JPA CriteriaQuery 的 ORDER BY 子句中重写子查询

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

I'm trying to write an SQL query using CriteriaQuery, but I'm having a hard time doing so.我正在尝试使用 CriteriaQuery 编写 SQL 查询,但我很难这样做。 This query basically gets a list of shipments and sorts them by their authorization date.这个查询基本上是获取一个发货清单,并按授权日期对它们进行排序。 This authorization date is represented as the date attribute of the first record in the status transition messages table with an initial status of 3 and a final status of 4. This is my query:此授权日期表示为状态转换消息表中第一条记录的日期属性,初始状态为 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;

I've tried multiple different solutions, but none have worked so far.我尝试了多种不同的解决方案,但到目前为止都没有奏效。

My current iteration is as follows:我目前的迭代如下:

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);
    }

The problem with this solution is that the SQL query generated by the CriteriaQuery does not support subqueries in the ORDER BY clause, causing a parsing exception.这个方案的问题是CriteriaQuery生成的SQL查询不支持ORDER BY子句中的子查询,导致解析异常。

My CriteriaQuery-fu is not good enough to help you with that part, but you could rewrite your SQL query to this:我的 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;

To me, this quite likely seems to be a faster solution anyway than running a correlated subquery in the ORDER BY clause, especially on RDBMS with less sophisticated optimisers.对我来说,这似乎是比在ORDER BY子句中运行相关子查询更快的解决方案,尤其是在具有不太复杂的优化器的 RDBMS 上。

So I attempted to follow @Lukas Eder solution and reached this solution:所以我尝试遵循@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);
    }
}

But now it's throwing this exception:但现在它抛出这个异常:

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

This happens because the query is only going to get distinct shipments later on and it's asking for the sorting column also appear in the select.发生这种情况是因为查询稍后只会获得不同的货物,并且它要求排序列也出现在选择中。 The problem is I don't know how to force CriteriaQuery to keep that column in the SELECT statement.问题是我不知道如何强制 CriteriaQuery 将该列保留在 SELECT 语句中。 It automatically only puts in the ORDER BY.它会自动只放入 ORDER BY。

Here's the JPQL query it's executing in my test:这是它在我的测试中执行的 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

and the generated SQL query:以及生成的 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