Calculated property with JPA / Hibernate

2019-01-01 05:27发布

问题:

My Java bean has a childCount property. This property is not mapped to a database column. Instead, it should be calculated by the database with a COUNT() function operating on the join of my Java bean and its children. It would be even better if this property could be calculated on demand / \"lazily\", but this is not mandatory.

In the worst case scenario, I can set this bean\'s property with HQL or the Criteria API, but I would prefer not to.

The Hibernate @Formula annotation may help, but I could barely find any documentation.

Any help greatly appreciated. Thanks.

回答1:

JPA doesn\'t offer any support for derived property so you\'ll have to use a provider specific extension. As you mentioned, @Formula is perfect for this when using Hibernate. You can use an SQL fragment:

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

Or even complex queries on other tables:

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

Where id is the id of the current entity.

The following blog post is worth the read: Hibernate Derived Properties - Performance and Portability.

Without more details, I can\'t give a more precise answer but the above link should be helpful.

See also:

  • Section 5.1.22. Column and formula elements (Hibernate Core documentation)
  • Section 2.4.3.1. Formula (Hibernate Annotations documentation)


回答2:

As explained in this article, you have three options:

  • either you are calculating the attribute using a @Transient method
  • you can also use @PostLoad entity listener
  • or you can use the Hibernate specific @Formula annotation

While Hibernate allows you to use @Formula, with JPA, you can use the @PostLoad callback to populate a transient property with the result of some calculation:

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

For more complex queries, you can use the Hibernate @Formula, as explained in this article:

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