web-dev-qa-db-fra.com

Comment mapper des propriétés calculées avec JPA et Hibernate

Mon bean Java a une propriété childCount. Cette propriété est non mappée à une colonne de base de données . Au lieu de cela, il devrait être calculé par la base de données avec une fonction COUNT()opérant sur la jointure de mon bean Java et de ses enfants. Ce serait encore mieux si cette propriété pouvait être calculée sur demande/"paresseusement", mais ce n'est pas obligatoire.

Dans le pire des cas, je peux définir la propriété de ce bean avec HQL ou l'API Criteria, mais je préférerais ne pas le faire.

L'annotation Hibernate @Formula peut aider, mais je pouvais à peine trouver de la documentation.

Toute aide grandement appréciée. Merci.

91
Francois

JPA n'offre aucun support pour les propriétés dérivées, vous devez donc utiliser une extension spécifique au fournisseur. Comme vous l'avez mentionné, @Formula est parfait pour cela lorsque vous utilisez Hibernate. Vous pouvez utiliser un fragment SQL:

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

Ou même des requêtes complexes sur d'autres tables:

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

id est la id de l'entité actuelle.

Le billet de blog suivant vaut la peine d'être lu: Propriétés dérivées d'Hibernate - Performances et portabilité .

Sans plus de détails, je ne peux pas donner de réponse plus précise, mais le lien ci-dessus devrait être utile.

Voir également:

145
Pascal Thivent

Comme expliqué dans cet article , vous avez trois options:

  • soit vous calculez l'attribut en utilisant une méthode @Transient
  • vous pouvez également utiliser @PostLoad entité écouteur
  • ou vous pouvez utiliser l'annotation @Formula spécifique à Hibernate

Bien qu'Hibernate vous permette d'utiliser @Formula , avec JPA, vous pouvez utiliser le callback @PostLoad pour remplir une propriété transient avec le résultat d'un calcul:

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

Pour des requêtes plus complexes, vous pouvez utiliser le code Hibernate @Formula, comme expliqué dans cet article :

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

Jetez un coup d’œil à Blaze-Persistence Entity Views qui fonctionne au-dessus de JPA et fournit un support DTO de première classe. Vous pouvez projeter n'importe quoi sur des attributs dans Entity Views et il pourra même réutiliser des nœuds de jointure existants pour des associations, si possible.

Voici un exemple de mapping

@EntityView(Order.class)
interface OrderSummary {
  Integer getId();
  @Mapping("SUM(orderPositions.price * orderPositions.amount * orderPositions.tax)")
  BigDecimal getOrderAmount();
  @Mapping("COUNT(orderPositions)")
  Long getItemCount();
}

Cela va générer une requête JPQL/HQL similaire à this

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

Voici un article de blog sur les fournisseurs de sous-requêtes personnalisés qui pourrait également vous intéresser: https://blazebit.com/blog/2017/entity-view-mapping-subqueries.html

0
Christian Beikov