【问题标题】:Hibernate Subquery Question休眠子查询问题
【发布时间】:2010-12-06 19:29:18
【问题描述】:

我希望这应该是一个简单的。

我有一张发票,该发票上有付款清单。

我正在尝试使用 Criteria API 返回发票列表及其付款总额。所以,在 SQL 中我想要这样的东西:

SELECT i.*, (SELECT SUM(PMT_AMOUNT) FROM INVOICE_PAYMENTS p WHERE p.INVOICE = i.INVOICE) FROM INVOICES i

我终其一生都无法弄清楚如何使用 Criteria API 实现这一目标。做类似的事情:

Criteria crit = session.createCriteria(Invoice.class)
criteria.setProjection(Projections.projectionList()
          .add(Projections.sum("payements.paymentAmount").as("paymentTotal"))

仅返回 1 行,其中包含所有发票的预计付款总额,这实际上是您所期望的,但这是我能得到的最接近的结果。

非常感谢任何帮助。

【问题讨论】:

    标签: java hibernate criteria subquery


    【解决方案1】:

    Criteria 有一种方法可以返回发票列表以及该发票的总付款。

    理论上,答案是您可以在投影查询中使用分组属性将结果分组为按发票支付的总金额。第二部分是您可以在 Invoice 上使用瞬态“totalPayment”值,并使用转换器选择投影到 Invoice 结构中。这比处理具有不同属性的 ArrayList 更容易,但取决于您需要将结果用于什么。

    为了证明这一点,这里是一个小型 Invoice 类的重要部分:

    public class Invoice{
       private String name;
    
       @Transient private int totalPayments;
       @OneToMany Set<Payment> payments = new HashSet<Payment>();
    
       // getters and setters
    ...
    }
    

    那么这就是你可以使用的标准

    Criteria criteria = session.createCriteria(Invoice.class)
                .createAlias("payments", "pay")
                .setProjection(Projections.projectionList()
                    .add(Projections.groupProperty("id"))
                    .add(Projections.property("id"), "id")
                    .add(Projections.property("name"), "name")
                    .add(Projections.sum("pay.total").as("totalPayments")))
                .setResultTransformer(Transformers.aliasToBean(Invoice.class));
    
    List<Invoice> projected = criteria.list();
    

    这是生成的sql

    Hibernate: 
       select this_.id as y0_, 
              this_.id as y1_, 
              this_.name as y2_, 
              sum(pay1_.total) as y3_ 
       from invoice this_ 
       inner join invoice_payment payments3_ on this_.id=payments3_.invoice_id 
       inner join payment pay1_ on payments3_.payments_id=pay1_.id 
       group by this_.id
    

    【讨论】:

      【解决方案2】:

      我很确定您不能在 Projection 中返回实体。

      有两种可能:

      • 运行两个条件查询,一个用于实际发票,一个用于总计
      • 使用 HQL 执行查询

      我没有对此进行测试,但它应该是这样的:

      select i, (select sum(p.amount) from InvoicePayments p where p.invoice = i.invoice) from Invoice i 
      

      必须等到明天,我有一个非常相似的数据结构在工作,我应该能够测试这个。

      【讨论】:

      • 不幸的是,HQL 在这里不是一个选项,而且我正在处理一个大型数据集,因此运行两个查询也不理想。
      • 将其作为休眠的本机 sql 查询是我能想到的唯一其他选择。试试这两个查询选项,你可能会对结果感到惊讶,db 将在两个语句中而不是一个语句中完成大致相同的工作,网络 IO 和解析的额外开销可能非常小。跨度>
      • 谢谢。我实际上决定通过使用 oracle 级别的视图来解决这个问题,然后将该视图映射到一个对象。我尝试使用本机 SQL 查询,但它根本没有表现好,很可能是由于大量重复连接。事实证明,我的最终查询无论如何都相当复杂,即使是 HQL 也可能很难让它工作。
      【解决方案3】:

      您还可以将@Formula 用于totalPayments 字段。缺点是,每次加载实体时都会计算“总和”。因此,您可以使用 LAZY @Formula - 进行构建时间增强或 Pawel Kepka 的技巧:http://justonjava.blogspot.com/2010/09/lazy-one-to-one-and-one-to-many.html 缺点是,您有更多 LAZY @Fromula 并且您只点击其中一个,所有这些都已加载。另一种解决方案可能是使用@MappedSuperclass 和更多子类。每个子类可能有不同的@Formula 字段。除了 DB 视图之外,还有一种解决方案:Hibernate @Subselect。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-07-20
        • 2013-01-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多