Cómo mapear propiedades calculadas con JPA e Hibernate

Resuelto Francois asked hace 14 años • 3 respuestas

Mi bean Java tiene una propiedad childCount. Esta propiedad no está asignada a una columna de base de datos . En cambio, debería ser calculado por la base de datos con una COUNT()función que opere en la unión de mi bean Java y sus hijos. Sería incluso mejor si esta propiedad pudiera calcularse según demanda/"perezosamente", pero esto no es obligatorio.

En el peor de los casos, puedo configurar la propiedad de este bean con HQL o la API Criteria, pero preferiría no hacerlo.

La @Formulaanotación de Hibernate puede ayudar, pero apenas pude encontrar documentación.

Cualquier ayuda es muy apreciada. Gracias.

Francois avatar Jun 07 '10 06:06 Francois
Aceptado

JPA no ofrece ningún soporte para propiedades derivadas, por lo que deberá utilizar una extensión específica del proveedor. Como mencionaste, @Formulaes perfecto para esto cuando se usa Hibernate. Puedes usar un fragmento de SQL:

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

O incluso consultas complejas en otras tablas:

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

¿ Dónde idestá el idde la entidad actual?

Vale la pena leer la siguiente publicación de blog: Propiedades derivadas de Hibernate: rendimiento y portabilidad .

Sin más detalles, no puedo dar una respuesta más precisa, pero el enlace anterior debería resultar útil.

Ver también:

  • Sección 5.1.22. Elementos de columna y fórmula (documentación de Hibernate Core)
  • Sección 2.4.3.1. Fórmula (documentación de anotaciones de Hibernate)
Pascal Thivent avatar Jun 06 '2010 23:06 Pascal Thivent

Tienes tres opciones:

  • o estás calculando el atributo usando un @Transientmétodo
  • También puedes usar @PostLoadel oyente de entidades.
  • o puedes usar la @Formulaanotación específica de Hibernate

Mientras que Hibernate te permite usar @Formula , con JPA puedes usar la devolución de llamada @PostLoad para completar una propiedad transitoria con el resultado de algún cálculo:

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

Entonces, puedes usar Hibernate @Formulade esta manera:

@Formula("""
    round(
       (interestRate::numeric / 100) *
       cents *
       date_part('month', age(now(), createdOn)
    )
    / 12)
    / 100::numeric
    """)
private double interestDollars;
Vlad Mihalcea avatar Mar 12 '2015 07:03 Vlad Mihalcea

Eche un vistazo a Blaze-Persistence Entity Views , que funciona sobre JPA y proporciona soporte DTO de primera clase. Puede proyectar cualquier cosa en atributos dentro de Entity Views e incluso reutilizará los nodos de unión existentes para asociaciones si es posible.

Aquí hay un ejemplo de mapeo.

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

Obtener esto generará una consulta JPQL/HQL similar a esta

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

Aquí hay una publicación de blog sobre proveedores de subconsultas personalizados que también podría resultarle interesante: https://blazebit.com/blog/2017/entity-view-mapping-subqueries.html

Christian Beikov avatar Jan 31 '2019 09:01 Christian Beikov