sábado, 27 de octubre de 2018

Etiqueta @Formula en Entidades Hibernate y uso en Criteria

La etiqueta @Formula permite asignar una fórmula a un atributo de una entidad para que se aplique en caso de seleccionarla en alguna consulta.
Por ejemplo, tengo una entidad con un atributo "fecha" mapeado con el campo "FECHA" de la tabla de base de datos. Tengo que poder realizar una consulta a base de datos agrupando las fechas de los registros por días, semanas, meses o años dependiendo de un filtro que marcará el usuario.
En principio es tan simple como utilizar la función "TRUNC" de SQL en la consulta para que me agrupe las fechas, pero también podemos declarar el "TRUNC" en la entidad a través de la etiqueta @Formula, utilizando atributos adicionales no mapeados con ningún campo de la base de datos.



El código sería:

Entidad:

@Column(name="FECHA")
private Date fecha;

/** Fecha agrupada por semana */
@Formula("TRUNC(FECHA,'WW')")
private Date fechaSemana;

/** Fecha agrupada por mes */
@Formula("TRUNC(FECHA,'MM')")
private Date fechaMes;

/** Fecha agrupada por año */
@Formula("TRUNC(FECHA,'YY')")
private Date fechaAnnio;

public Date getFecha() {
return fecha;
}

public void setFecha(Date fecha) {
this.fecha = fecha;
}

public Date getFechaMes() {
return fechaMes;
}

public void setFechaSemana(Date fechaSemana) {
this.fechaSemana = fechaSemana;
}

public Date getFechaMes() {
return fechaMes;
}

public void setFechaMes(Date fechaMes) {
this.fechaMes = fechaMes;
}

public Date getFechaAnnio() {
return fechaAnnio;
}

public void setFechaAnnio(Date fechaAnnio) {
this.fechaAnnio = fechaAnnio;
}

Tal y como está configurada la entidad, si yo en la consulta selecciono el atributo "fechaAnnio", lo que va a hacer la fórmula es agrupar las fechas por año y guardar el resultado en el atributo "fecha", que es el que sí está mapeado con la base de datos, siendo el atributo "fechaAnnio" un mero conversor para el atributo "fecha".

La consulta a través de Criteria en el DAO quedaría así:

CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<EstadisticasPortabilidad> criteriaQuery = builder.createQuery(EstadisticasPortabilidad.class);
Root<EstadisticasPortabilidad> rootEntity = criteriaQuery.from(EstadisticasPortabilidad.class);

switch (agrupacion) {
case Constants.AGRUPACION_SEMANA:
criteriaQuery.multiselect(rootEntity.get("operador").get("descripcion"), rootEntity.get("operador"), rootEntity.get("fechaSemana"),
builder.sum(rootEntity.get("cantidad")), rootEntity.get("tipoMensaje"))
.groupBy(rootEntity.get("operador").get("descripcion"), rootEntity.get("operador"), rootEntity.get("tipoMensaje"), rootEntity.get("fechaSemana"))
.orderBy(builder.desc(rootEntity.get("fechaSemana")), builder.asc(rootEntity.get("operador").get("descripcion")));
break;
case Constants.AGRUPACION_MES:
criteriaQuery.multiselect(rootEntity.get("operador").get("descripcion"), rootEntity.get("operador"), rootEntity.get("fechaMes"),
builder.sum(rootEntity.get("cantidad")), rootEntity.get("tipoMensaje"))
.groupBy(rootEntity.get("operador").get("descripcion"), rootEntity.get("operador"), rootEntity.get("tipoMensaje"), rootEntity.get("fechaMes"))
.orderBy(builder.desc(rootEntity.get("fechaMes")), builder.asc(rootEntity.get("operador").get("descripcion")));
break;
case Constants.AGRUPACION_ANNIO:
criteriaQuery.multiselect(rootEntity.get("operador").get("descripcion"), rootEntity.get("operador"), rootEntity.get("fechaAnnio"),
builder.sum(rootEntity.get("cantidad")), rootEntity.get("tipoMensaje"))
.groupBy(rootEntity.get("operador").get("descripcion"), rootEntity.get("operador"), rootEntity.get("tipoMensaje"), rootEntity.get("fechaAnnio"))
.orderBy(builder.desc(rootEntity.get("fechaAnnio")), builder.asc(rootEntity.get("operador").get("descripcion")));
break;
default:
criteriaQuery.multiselect(rootEntity.get("operador").get("descripcion"), rootEntity.get("operador"), rootEntity.get("fecha"),
builder.sum(rootEntity.get("cantidad")), rootEntity.get("tipoMensaje"))
.groupBy(rootEntity.get("operador").get("descripcion"), rootEntity.get("operador"), rootEntity.get("tipoMensaje"), rootEntity.get("fecha"))
.orderBy(builder.desc(rootEntity.get("fecha")), builder.asc(rootEntity.get("operador").get("descripcion")));
}

Predicate predicateFechas = builder.between(rootEntity.get("fecha"), startdate, enddate);
criteriaQuery.where(predicateFechas);

Query<EstadisticasPortabilidad> query = session.createQuery(criteriaQuery);
query.setFirstResult(first);
query.setMaxResults(pageSize);

List<EstadisticasPortabilidad> listaEstadisticas = query.getResultList();

if (listaEstadisticas != null) {
for (EstadisticasPortabilidad estadistica : listaEstadisticas) {
Stats stat = new Stats(estadistica.getOperador().getCodigo(), estadistica.getOperador().getDescripcion(),
estadistica.getFecha(), estadistica.getSp(), estadistica.getAns(),
estadistica.getEsc(), estadistica.getCp(),
estadistica.getAncp(), estadistica.getEcpc());
lista.add(stat);
}
}

Como se puede ver, dependiendo de la agrupación seleccionada, se selecciona un atributo de fecha u otro, pero el valor final siempre se guardará en el atributo "fecha", de tal forma que siempre podemos utilizar ese atributo posteriormente independientemente de la agrupación escogida al hacer la consulta.

No hay comentarios:

Publicar un comentario