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


104

我的Java bean具有childCount属性。此属性未映射到数据库列。取而代之的是,它应该由数据库通过COUNT()对Java bean及其子级的联接进行操作函数计算。如果可以按需/“懒惰地”计算此属性,那就更好了,但这不是强制性的。

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

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

任何帮助,不胜感激。谢谢。

Answers:


162

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;

idid当前实体的。

以下博客文章值得一读:Hibernate Derived Properties-Performance and Portability

没有更多详细信息,我无法给出更准确的答案,但是以上链接应该会有所帮助。

也可以看看:


谢谢Pascal。您知道以下原因为何不起作用吗?@Formula(value =“(从ic内部联接c中选择count(),其中ic.category_id = c.id和c.id = id)”)public Integer getCountInternal(){return countInternal; 该查询很好,我看到它正在日志中运行。映射似乎还可以:主org.hibernate.cfg.Ejb3Column-绑定公式(选择count()等等等等但是countInternal仍然设置为其初始值(-1):(我已经尝试使用字段注释而不是getter注释,但是它仍然无法正常工作。
弗朗索瓦

谢谢,这确实对我的任务很有帮助!
Mythul

13
@NeilMcGuigan:@Formula注释使用SQL,而不是HQL。
user1438038'9

7
刚刚了解了在查询中加上括号的重要性。总是以SQL异常结尾,因为加载主机对象时,此查询当然会成为子查询。MySQL不喜欢没有括号的内联选择。
对不起,2015年


53

本文所述,您有三个选择:

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

虽然Hibernate允许您将@Formula与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;

7
但是,这不允许进行更复杂的查询
Hubert Grzeskowiak

2
我更新了答案,所以现在您也可以解决更复杂的查询。
Vlad Mihalcea

1

看一下在JPA之上工作并提供一流DTO支持的Blaze-Persistence实体视图。您可以将任何项目投影到实体视图中的属性,并且如果可能的话,它甚至可以将现有的联接节点重新用于关联。

这是一个示例映射

@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


0

我已经在我的代码上尝试过

 @Formula(value = "(select DATEDIFF(expiry_date,issue_date)  from document_storage)")
    private String  myColumn;

在运行并显示我的视图时甚至在尝试在小胡子上显示列时出现的错误是这样的

java.lang.NullPointerException
    at java.base/java.lang.String$CaseInsensitiveComparator.compare(String.java:1241)
    at java.base/java.lang.String$CaseInsensitiveComparator.compare(String.java:1235)
    at java.base/java.util.TreeMap.getEntryUsingComparator(TreeMap.java:374)
    at java.base/java.util.TreeMap.getEntry(TreeMap.java:343)
    at java.base/java.util.TreeMap.get(TreeMap.java:277)
    at com.mysql.cj.result.DefaultColumnDefinition.findColumn(DefaultColumnDefinition.java:182)

我问的问题是有没有办法使用JPA / Hibernate和mySQL获取Calculated列的值?

我究竟做错了什么 ?

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.