如何使用JPA和Hibernate映射计算属性

135

我的Java Bean有一个childCount属性。这个属性没有映射到数据库列,相反,应该使用COUNT()函数在Java Bean和其子节点的连接上进行计算,由数据库来计算。如果可以按需/“懒惰”方式计算此属性会更好,但这不是必须的。

在最坏的情况下,我可以使用HQL或Criteria API设置此Bean的属性,但我希望不要这样做。

Hibernate的@Formula注释可能有所帮助,但我几乎找不到任何文档。

非常感谢您的帮助。谢谢。

3个回答

205

JPA不支持派生属性,因此您需要使用提供程序特定的扩展。如您所述,当使用Hibernate时,@Formula非常适合此用途。您可以使用SQL片段:

@Formula("PRICE*1.155")
private float finalPrice;

甚至可以对其他表进行复杂的查询:

@Formula("(select min(o.creation_date) from Orders o where o.customer_id = id)")
private Date firstOrderDate;

其中id是当前实体的id

推荐阅读以下博客文章:Hibernate Derived Properties - Performance and Portability

没有更多细节,我无法给出更精确的答案,但上面的链接应该会有所帮助。

另见:


谢谢,这真的帮了我很多,让我完成了任务! - Mythul
1
你是行家Pascal。在这个公式里的SQL是真正的SQL还是HQL?谢谢。 - Neil McGuigan
21
“@Formula”注解使用的是SQL而不是HQL。 - user1438038
8
刚刚学到了在查询语句中加上括号的重要性。之前没有加括号,导致当宿主对象被加载时,这个查询就变成了子查询,最终导致 SQL 异常。MySQL 不喜欢没有括号的内联查询。 - sorrymissjackson
1
文章的新链接是:http://blog.eyallupu.com/2009/07/hibernate-derived-properties.html - Adnan
显示剩余2条评论

77

你有三个选择:

  • 要么使用@Transient方法计算属性。
  • 你也可以使用@PostLoad实体监听器。
  • 或者你可以使用Hibernate特定的@Formula注释。

Hibernate允许你使用@Formula,而在JPA中,你可以使用@PostLoad回调来用某些计算结果填充一个transient属性:

@Column(name = "price")
private Double price;

@Column(name = "tax_percentage")
private Double taxes;

@Transient
private Double priceWithTaxes;

@PostLoad
private void onLoad() {
    this.priceWithTaxes = price * taxes;
}

因此,您可以像这样使用 Hibernate @Formula

@Formula("""
    round(
       (interestRate::numeric / 100) *
       cents *
       date_part('month', age(now(), createdOn)
    )
    / 12)
    / 100::numeric
    """)
private double interestDollars;

9
然而,这不能支持更复杂的查询。 - Hubert Grzeskowiak
2
我更新了答案,现在你也有一个更复杂查询的解决方案。 - Vlad Mihalcea
1
我可以在使用了@PostLoad设置的瞬态字段上使用order by吗? - Jinsu

3
看看Blaze-Persistence Entity Views,它在JPA之上运行,并提供一流的DTO支持。您可以将任何内容投射到Entity Views中的属性中,如果可能,它甚至会重用现有的关联连接节点。
以下是一个示例映射:
@EntityView(Order.class)
interface OrderSummary {
  Integer getId();
  @Mapping("SUM(orderPositions.price * orderPositions.amount * orderPositions.tax)")
  BigDecimal getOrderAmount();
  @Mapping("COUNT(orderPositions)")
  Long getItemCount();
}

最初的回答:获取此内容将生成类似于此的JPQL/HQL查询。
SELECT
  o.id,
  SUM(p.price * p.amount * p.tax),
  COUNT(p.id)
FROM
  Order o
LEFT JOIN
  o.orderPositions p
GROUP BY
  o.id

这是一篇有关自定义子查询提供程序的博客文章,可能对您也很有趣: https://blazebit.com/blog/2017/entity-view-mapping-subqueries.html。"Original Answer"翻译成"最初的回答"。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接