Cómo mapear propiedades calculadas con JPA e Hibernate
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 @Formula
anotación de Hibernate puede ayudar, pero apenas pude encontrar documentación.
Cualquier ayuda es muy apreciada. Gracias.
JPA no ofrece ningún soporte para propiedades derivadas, por lo que deberá utilizar una extensión específica del proveedor. Como mencionaste, @Formula
es 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 id
está el id
de 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)
Tienes tres opciones:
- o estás calculando el atributo usando un
@Transient
método - También puedes usar
@PostLoad
el oyente de entidades. - o puedes usar la
@Formula
anotació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 @Formula
de esta manera:
@Formula("""
round(
(interestRate::numeric / 100) *
cents *
date_part('month', age(now(), createdOn)
)
/ 12)
/ 100::numeric
""")
private double interestDollars;
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