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

Posted

技术标签:

【中文标题】如何使用 JPA 和 Hibernate 映射计算的属性【英文标题】:How to map calculated properties with JPA and Hibernate 【发布时间】:2011-02-28 12:03:50 【问题描述】:

我的 Java bean 有一个 childCount 属性。此属性未映射到数据库列。相反,它应该由数据库使用COUNT() 函数在我的Java bean 及其子项的连接上运行。如果这个属性可以按需/“懒惰”计算会更好,但这不是强制性的。

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

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

非常感谢任何帮助。谢谢。

【问题讨论】:

【参考方案1】:

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 Derived Properties - Performance and Portability。

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

另见:

5.1.22. Column and formula elements 部分(Hibernate Core 文档) 2.4.3.1. Formula 部分(Hibernate Annotations 文档)

【讨论】:

谢谢帕斯卡。您知道为什么以下内容不起作用吗? @Formula(value = "(select count() from ic inner join c where ic.category_id = c.id and c.id = id)") public Integer getCountInternal() return countInternal;查询没问题,我看到它正在日志中运行。映射似乎没问题: main org.hibernate.cfg.Ejb3Column - 绑定公式(select count() blah blah blah 但是 countInternal 仍然设置为其初始值 (-1) :( 我尝试使用字段注释而不是getter 注释,但它仍然不起作用。 你是帕斯卡。公式中的 SQL 是真正的 SQL 还是 HQL?谢谢 @NeilMcGuigan:@Formula 注释使用 SQL,而不是 HQL。 刚刚了解到在查询周围加上括号的重要性。总是以 SQL 异常结束,因为当加载主机对象时,这个查询当然会成为子查询。 mysql 不喜欢不带括号的内联选择。 文章新增链接为:blog.eyallupu.com/2009/07/hibernate-derived-properties.html【参考方案2】:

您有三个选择:

要么您正在使用@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;

【讨论】:

然而这不允许更复杂的查询 我更新了答案,所以现在您也有了更复杂查询的解决方案。 我可以将 order by 与 @PostLoad 设置的瞬态字段一起使用吗?【参考方案3】:

看看Blaze-Persistence Entity Views,它在 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

【讨论】:

以上是关于如何使用 JPA 和 Hibernate 映射计算的属性的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 JPA 和 Hibernate 将 MySQL JSON 列映射到 Java 实体属性

如何在pojo中用jpa或hibernate映射json字段?

如何使用 Spring Data JPA(Hibernate) 跨映射表过滤关联实体?

如何使用 JPA/Hibernate 注释将 MySQL char(n) 列映射到实例变量?

使用 JPA 和 Hibernate 将 Java 布尔值映射到 Oracle Number 列

如何使用 hibernate/jpa 注释将一个类映射到不同的表