【问题标题】:QueryDsl - How create inner join with sorted and grouped tableQueryDsl - 如何使用排序和分组表创建内部连接
【发布时间】:2025-12-05 00:55:01
【问题描述】:

我必须找到销售了一些itemType 的销售人员。我创建了方法(见下文)。

但是客户告诉我他想找到最后销售的销售人员itemType。 数据库架构:

我的尝试:我们在表 ORDERS 日期列中,所以在普通 SQL 查询中我可以执行双子查询,它应该可以工作。

双重,因为我首先按日期排序,然后按销售员分组 - 返回的列表仅包含最后售出的物品。

SELECT * 
FROM SALESMEN   
JOIN 
    (SELECT *
     FROM 
         (SELECT *
          FROM ORDERS
          ORDER BY ORDERS.date)
     GROUP BY ORDERS.salesman_id) ON SALESMEN.id = ORDERS.salesman_id
WHERE ORDERS.item_type = "CAR" 

很遗憾,queryDSL 只能在 IN 子句中进行子查询,而不能在 FROM 中。

我花了很多时间来寻找解决方案,在我看来,使用queryDSL 来获得排序和分组列表并在一个查询中将其与另一个表连接起来是根本不可能的。

但也许有经验的人有任何想法,也许解决方案比我想象的要简单:D

public List<SalesmanEntity> findSalesman(SalesmanSearchCriteriaTo criteria) {
    SalesmanEntity salesmanEntity = Alias.alias(SalesmanEntity.class);
    EntityPathBase<SalesmanEntity> alias = Alias.$(salesman);   
    JPAQuery<SalesmanEntity> query = new JPAQuery<SalesmanEntity>(getEntityManager()).from(alias);

    ... a lot of wird IF's....

    if (criteria.getLastSoldItemTyp() != null) {
        OrderEntity order = Alias.alias(OrderEntity.class);
        EntityPathBase<OrderEntity> aliasOrder = Alias.$(order);
    
        query.join(aliasOrder)
        .on(Alias.$(salesman.getId()).eq(Alias.$(order.getSalesmanId())))
        .where(Alias.$(order.getItemTyp()).eq(criteria.getLastSoldItemTyp()));
    }
    
    return query.fetch();
}

环境:

  • Java 1.8
  • SpringBoot 2.0.9
  • 查询DSL 4.1.4

【问题讨论】:

    标签: java spring querydsl


    【解决方案1】:

    这不是 QueryDSL 的限制,而是 JPQL(JPA 的查询语言)的限制。例如,SQL 确实允许 FROM 子句中的子查询,因此querydsl-sql 也允许它。使用普通的 JPA,甚至 Hibernate 的专有 HQL 都无法完成。然后,您将不得不编写本机 SQL 查询。为此,您可以查看@NamedNativeQuery

    可以使用Blaze-Persistence 使用公用表表达式 (CTE) 在 JPA 之上添加子查询。 Blaze-Persistence 还附带一个可选的 QueryDSL 集成。

    使用该扩展库,您只需编写以下代码:

    QRecursiveEntity recursiveEntity = new QRecursiveEntity("t");
    
    List<RecursiveEntity> fetch = new BlazeJPAQuery<>(entityManager, cbf)
    .select(recursiveEntity)
    .from(select(recursiveEntity)
        .from(recursiveEntity)
        .where(recursiveEntity.parent.name.eq("root1"))
        .orderBy(recursiveEntity.name.asc())
        .limit(1L), recursiveEntity)
    .fetch();
    

    或者,在使用 Hibernate 时,您可以将子查询映射为实体,然后在查询中关联它。使用它您可以获得相同的结果,但您将无法在子查询中引用任何外部变量,也无法对子查询进行参数化。但是,上述方法都可以使用这两个功能!

      @Entity
      @Subselect("SELECT salesman_id, sum(amount) FROM ( SELECT * FROM ORDERS ORDER BY ORDERS.date ) GROUP BY ORDERS.salesman_id")
      class OrdersBySalesMan {
            @Id @Column(name = "salesman_id") Long salesmanId;
            @Basic BigDecimal amount; // or something similarly
      }
    

    然后在您的查询中:

    .from(QSalesman.salesman)
        .innerJoin(QOrdersBySalesMan.ordersBySalesMan)
        .on(ordersBySalesMan.salesmanId.eq(salesman.id))
    

    【讨论】:

      最近更新 更多