将 JPQL 查询转换为条件查询

问题描述 投票:0回答:1

为了学习 JPA,我正在尝试探索各种执行查询的方法。我有按预期工作的本机 SQL 和 JPQL 查询。现在我正在尝试通过 Criteria Query 方法实现相同的目标。 我的实体:

@Entity
public class GiftCertificate {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    private String description;

    @Column(precision = 10, scale = 2)
    private BigDecimal price;

    private Short duration;

    private LocalDateTime createDate;

    private LocalDateTime lastUpdateDate;

    @ManyToMany
    @JoinTable(
        name = "gift_certificate_tag",
        joinColumns = @JoinColumn(name = "gift_certificate_id"),
        inverseJoinColumns = @JoinColumn(name = "tag_id")
    )
    private Set<Tag> tags = new HashSet<>();

    public Set<Tag> getTags() {
        return Collections.unmodifiableSet(tags);
    }
}

@Entity
@Table(name = "ORDERS")
public class Order {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    private User user;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    private GiftCertificate giftCertificate;

    private LocalDateTime orderDate;

    private BigDecimal orderCost;
}

@Entity
@Table(name = "tag", schema = "public")
public class Tag {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    //@Basic(optional = false)
    //@NotNull
    @Column(nullable = false)
    private String name;
}

@Entity
@Table(name = "users", schema = "public")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String userName;

}

任务是获取所有订单中成本最高的用户使用最广泛的标签

工作SQL:

SELECT *
        FROM   tag
               JOIN (SELECT tag_id,
                            Count (gift_certificate_tag.gift_certificate_id) AS
                            cert_count
                     FROM   gift_certificate_tag
                            JOIN orders
                              ON gift_certificate_tag.gift_certificate_id =
                                 orders.gift_certificate_id
                            JOIN (SELECT user_id,
                                         Sum (order_cost) AS all_orders_cost
                                  FROM   orders
                                  GROUP  BY user_id
                                  ORDER  BY all_orders_cost DESC
                                  LIMIT  1) AS orders_sum
                              ON orders_sum.user_id = orders.user_id
                     GROUP  BY tag_id
                     ORDER  BY cert_count DESC
                     LIMIT  1) AS result
                 ON result.tag_id = tag.id

工作 JPQL:

SELECT new Tag(t.id, t.name)
                FROM GiftCertificate gc
                JOIN gc.tags t
                JOIN Order o ON o.giftCertificate = gc
                JOIN o.user u
                WHERE u = (
                      SELECT ou
                      FROM Order o2
                      JOIN o2.user ou
                      GROUP BY ou
                      HAVING SUM(o2.orderCost) = (
                            SELECT MAX(totalOrderCost)
                            FROM (
                                  SELECT SUM(o3.orderCost) AS totalOrderCost
                                  FROM Order o3
                                  GROUP BY o3.user
                            ) subquery
                      )
                )
                GROUP BY t.id, t.name
                ORDER BY COUNT(gc) DESC

不工作条件查询:

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<Tag> cq = cb.createQuery(Tag.class);
        Root<Order> order = cq.from(Order.class);
        Join<Order, GiftCertificate> giftCertificate = order.join("giftCertificate");
        Join<GiftCertificate, Tag> tag = giftCertificate.join("tags");
        Join<Order, User> user = order.join("user");

        //sub-query to find user's orders sums
        Subquery<BigDecimal> querySumOfOrders = cq.subquery(BigDecimal.class);
        Root<Order> subOrderSumOfOrders = querySumOfOrders.from(Order.class);
        Join<Order, User> subUserSumOfOrders = subOrderSumOfOrders.join("user");
        querySumOfOrders.select(cb.sum(subOrderSumOfOrders.get("orderCost")))
                        .groupBy(subUserSumOfOrders);

        //sub-query to find max cost of the user's orders sum
        Subquery<BigDecimal> queryMaxOrdersSum = cq.subquery(BigDecimal.class);
        queryMaxOrdersSum.from(Order.class);
        queryMaxOrdersSum.select(cb.max(querySumOfOrders));

        //sub-query to find user with most cost of all orders
        Subquery<User> queryMostSpentUser = cq.subquery(User.class);
        Root<Order> subOrderMostSpentUser = queryMostSpentUser.from(Order.class);
        Join<Order, User> subUserMostSpentUser = subOrderMostSpentUser.join("user");
        queryMostSpentUser.select(subUserMostSpentUser)
                          .having(cb.equal(cb.sum(subOrderMostSpentUser.get("orderCost")), cb.max(queryMaxOrdersSum)))
                          .groupBy(subUserMostSpentUser);
        //main query
        cq.select(tag)
          .where(cb.equal(user, queryMostSpentUser))
          .orderBy(cb.desc(cb.count(giftCertificate)))
          .groupBy(tag);
        List<Tag> tagList = entityManager.createQuery(cq)
                                         .setMaxResults(1)
                                         .getResultList();

Hibernate根据条件查询生成的SQL:

select
        t1_1.id,
        t1_1.name 
    from
        public.orders o1_0 
    join
        public.gift_certificate g1_0 
            on g1_0.id=o1_0.gift_certificate_id 
    join
        (public.gift_certificate_tag t1_0 
    join
        public.tag t1_1 
            on t1_1.id=t1_0.tag_id) 
                on g1_0.id=t1_0.gift_certificate_id 
        join
            public.users u1_0 
                on u1_0.id=o1_0.user_id 
        where
            u1_0.id=(
                select
                    u2_0.id 
                from
                    public.orders o2_0 
                join
                    public.users u2_0 
                        on u2_0.id=o2_0.user_id 
                group by
                    1 
                having
                    sum(o2_0.order_cost)=(
                        select
                            max((select
                                sum(o4_0.order_cost) 
                            from
                                public.orders o4_0 
                            join
                                public.users u3_0 
                                    on u3_0.id=o4_0.user_id 
                            group by
                                o4_0.user_id)) 
                        from
                            public.orders o3_0)
                    ) 
                group by
                    1,
                    2 
                order by
                    count(o1_0.gift_certificate_id) desc fetch first ? rows only

问题出在以下 SQL 块:

                select max((select
                                sum(o4_0.order_cost) 
                            from
                                public.orders o4_0 
                            join
                                public.users u3_0 
                                    on u3_0.id=o4_0.user_id 
                            group by
                                o4_0.user_id))

这会导致 org.postgresql.util.PSQLException: ERROR: more than one row returned by a subquery used as an expression

问题是如何进行条件查询而不是生成与 JPQL 查询相同的 SQL:

                        select max(subquery1_0.totalOrderCost) 
                        from
                            (select
                                sum(o3_0.order_cost) 
                            from
                                public.orders o3_0 
                            group by
                                o3_0.user_id) subquery1_0(totalOrderCost)
java hibernate jpa criteria
1个回答
1
投票

“JPQL”(实际上是HQL)只能与Hibernate 6+一起使用,因为在此之前,Hibernate 不支持 from 子句中的子查询。要使用 JPA Criteria 中的该功能,您必须使用 Hibernate JPA Criteria 扩展。

    HibernateCriteriaBuilder cb = entityManager.unwrap(Session.class).getCriteriaBuilder();
    JpaCriteriaQuery<Tag> cq = cb.createQuery(Tag.class);
    JpaRoot<Order> order = cq.from(Order.class);
    JpaJoin<Order, GiftCertificate> giftCertificate = order.join("giftCertificate");
    JpaJoin<GiftCertificate, Tag> tag = giftCertificate.join("tags");
    JpaJoin<Order, User> user = order.join("user");

    //sub-query to find user's orders sums
    JpaSubquery<Tuple> querySumOfOrders = cq.subquery(Tuple.class);
    JpaRoot<Order> subOrderSumOfOrders = querySumOfOrders.from(Order.class);
    querySumOfOrders.multiselect(cb.sum(subOrderSumOfOrders.get("orderCost")).alias("theSum"))
                    .groupBy(subOrderSumOfOrders.get("user"));

    //sub-query to find max cost of the user's orders sum
    JpaSubquery<BigDecimal> queryMaxOrdersSum = cq.subquery(BigDecimal.class);
    JpaRoot<Tuple> queryMaxOrders = queryMaxOrdersSum.from(querySumOfOrders);
    queryMaxOrdersSum.select(cb.max(queryMaxOrders.get("theSum")));

    //sub-query to find user with most cost of all orders
    JpaSubquery<User> queryMostSpentUser = cq.subquery(User.class);
    JpaRoot<Order> subOrderMostSpentUser = queryMostSpentUser.from(Order.class);
    JpaJoin<Order, User> subUserMostSpentUser = subOrderMostSpentUser.join("user");
    queryMostSpentUser.select(subUserMostSpentUser)
                      .having(cb.equal(cb.sum(subOrderMostSpentUser.get("orderCost")), cb.max(queryMaxOrdersSum)))
                      .groupBy(subUserMostSpentUser);
    //main query
    cq.select(tag)
      .where(cb.equal(user, queryMostSpentUser))
      .orderBy(cb.desc(cb.count(giftCertificate)))
      .groupBy(tag);
    List<Tag> tagList = entityManager.createQuery(cq)
                                     .setMaxResults(1)
                                     .getResultList();
© www.soinside.com 2019 - 2024. All rights reserved.