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

我的爪哇豆有一个 child Count 属性。此属性是 未映射到数据库列。相反,它应该是在 Javabean 及其子级的连接上运行的 使用 COUNT()函数由数据库计算。如果这个属性可以根据需要/“惰性”计算,那就更好了,但是这不是强制的。

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

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

非常感谢你的帮助,谢谢。

156126 次浏览

JPA 不提供对派生属性的任何支持,因此必须使用特定于提供程序的扩展。正如您所提到的,@Formula在使用 Hibernate 时非常适合这种情况。可以使用 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 衍生特性-性能和便携性

没有更多的细节,我不能给出一个更精确的答案,但以上的链接应该是有帮助的。

参见:

你有三个选择:

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

虽然 Hibernate 允许您使用 @ 配方奶粉,但是对于 JPA,您可以使用 @ PostLoad回调来填充 暂时的属性,从而得到一些计算结果:

@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;

看看 Blaze-持久化实体视图,它在 JPA 之上工作,并提供一流的 DTO 支持。您可以将任何东西投影到实体视图中的属性,如果可能的话,它甚至会重用现有的连接节点来进行关联。

下面是一个映射示例

@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