Resolviendo la consulta JPA encontrando la última entrada en la lista conectada
Pregunta
Se da la siguiente estructura de clase:
class Job
{
String description;
Collection<JobHistory> history;
}
class JobHistory
{
Date assignDate;
User jobOwner;
}
class JobOwner
{
String name;
String id;
}
Esta estructura de clases es accesible en el db a través de JPA. En la capa DAO puedo escribir consultas en sintaxis JPA.
El problema: quiero una lista con las entradas Job
y JobHistory
para un propietario determinado con una identificación dada y quién es el último en el Jobhistory del trabajo (ordenado por asignar fecha). Suena bastante complicado, quizás más simple: darme todos los trabajos y
JobHistory
donde el propietario especificado es el propietario real del trabajo.
Actualización: para mayor claridad, cambiaré ligeramente los nombres de las clases.
class Job
{
String description;
Collection<JobOwnerHistory> history;
}
class JobOwnerHistory
{
Date assignDate;
User jobOwner;
}
class JobOwner
{
String name;
String id;
}
Cada Job
tiene un historial de sus propietarios ordenados por asignar fecha
. El propietario real recibió el último trabajo asignado (es decir, MAX (asignar fecha)
). Quiero encontrar para cada trabajo la entrada JobOwnerHistory
con MAX (asignar fecha)
para un usuario específico User
.
Solución
Prueba:
SELECT j, j.history FROM Job j JOIN User u WHERE u.name = :name
Si tuviera que hacer esto en EclipseLink, lo cambiaría ligeramente:
public List<Job> getAllJobsForUser(String username) {
List<Job> jobs = entityManager
.createQuery("SELECT j FROM Job j JOIN User u WHERE u.name = :name")
.setParameter("name", username)
.setHint(QueryHints.BATCH, "j.history")
.queryForList();
}
¿La diferencia? En la primera versión, está devolviendo dos objetos, por lo que debe recuperarlos de una lista o matrices de objetos, mientras que en la segunda, la sugerencia de consulta solo carga todos los historiales de trabajo de una relación de uno a muchos (supuestamente) perezosa .
No sé si Hibernate tiene un equivalente a esto. Toplink Essentials no lo hace. Pero es una de mis características favoritas de EclipseLink.
Ah, y obviamente puede (y probablemente debería) usar una consulta con nombre en lugar de una consulta ad hoc como lo he hecho (ya que pueden verificarse durante la compilación).
Otros consejos
Encontré la siguiente respuesta para la consulta:
SELECT j, h FROM Job j JOIN j.history h JOIN h.jobOwner u
WHERE u.name = :name AND
(SELECT MAX(h2.assignDate) FROM Job j2 JOIN j2.history h2
WHERE h2 member of j.history) = h.assignDate
La parte más importante de la consulta es la subselección con MAX(h2.assignDate)
porque quiero obtener el trabajo y la entrada más reciente en el historial del propietario .