Hace tiempo que vengo usando JPA y he notado que el uso de getSingleResult y getResultList a menudo se confunde. En el javadoc no hay una explicación clara de que usar:
- getResultList()
Execute a SELECT query and return the query results as an untyped List.
- getSingleResult()
Execute a SELECT query that returns a single untyped result.
Así que esto no es de mucha ayuda. Veamos que podemos hacer.
Ambos métodos son usados para recibir Entities de la base de datos. Cuando usamos getResultList no sabemos la cantidad de resultados que vamos a recuperar, en cambio getSingleResult se utiliza para recuperar exactamente un resultado(fila).
La duda que surge es ¿en que casos nosotros queremos recuperar solo una fila? Un ejemplo perfecto para este caso es cuando usamos consultas con el ID único(findById), donde recuperamos un registro basados en su identificador único.
Hay tres maneras de hacer esto, la mas adecuada sería usar el método EntityManger.find(). Este método retorna una instancia del tipo de la entidad cuando esta es encontrada. Las otras dos maneras es mediante un NamedQuery. Por que usaríamos un NamedQuery cuando tenemos el método EntityManger.find()?. A veces tenemos que recuperar una colección «perezosa»(lazzy) agregando a la consulta un «join». Otro ejemplo sería cuando nosotros tenemos una clave compuesta, podríamos utilizar el método EntityManger.find(), pero a menudo veo el uso de NamedQuery en estos casos.(Mala práctica de programación, no lo hagas). Creamos nuestro NamedQuery.
[java]
final Query query = getEntityManager().createNamedQuery("Entity.findById");
query.setParameter("id", id);
[/java]
La reacción más natural es la de llamar a la getSingleResult. Esto devuelve una sola fila y el resultado de una consulta «findById» debe ser sólo una fila. Pero ¿y si la fila no está en la base de datos? Recibimos una excepción sin control: NoResultException. Es esto lo que esperabas?
Si nos fijamos en el libro «Effective Java« de Joshua Bloch: «Use checked exceptions for conditions from wich the caller can reasonably be expected to recover. Use runtime exceptions to indicate programming errors»(Use excepciones comprobadas para condiciones de las cuales el usuario razonablemete espera recuperarse. Use excepciones de tiempo de ejecución para indicar errores de programación).
Que nos quiere decir?. Cuando una consulta getSingleResult no devuelve nada, obtenemos una excepción no controlada, pues de un error del programador no hay forma de recuperarse. Esto no es correcto. Nunca sabemos a ciencia cierta que nos devolverá la base de datos por lo que para este caso una excepción no controlada parece una mala elección.
El único uso para getSingleResult es cuando ejecutamos una consulta escalar, por ejemplo: count, sum, avg. Esta consulta siempre devolverá una fila, caso contrario estamos ante un caso excepcional, por lo que la excepción está permitida.
Asi que ¿Cómo resolver nuestra consulta con NamedQuery?, fácil:
[java]
List results = query.getResultList();
Entity foundEntity = null;
if(!results.isEmpty()){
// ignores multiple results
foundEntity = results.get(0);
}
[/java]
En este caso la lista contiene datos o esta vacía(isEmpty), no hay mas vuelta que dar. Luego si la lista no está vacía simplemente con un get(0) obtenemos nuestro valor, no hay «sorpresas«, si la base de datos no devuelve ninguna fila es fácilmente controlable por el programador.
Espero les sirva, cualquier cosa nos vemos en los comentarios.
Fuente : http://sysout.be/2011/03/09/why-you-should-never-use-getsingleresult-in-jpa/
Interesante el comentario, tienes razón acerca del uso del getSingleResult(), es cierto que de no haber el registro que buscas en la Base de Datos te devuelve un «No entity found for query», si generalmente se recomienda el uso de getResultList() para poder controlar el isEmpty().
Muy buen punto, gracias por el aporte.
Saludos Alejo.
Gracias amigo Armando, después de esta entrada algunos me han dicho que el singleresult es importante tal como está porque si se lo usa para recuperar un valor único como un ID y si no existe el error debería avisarnos, yo sigo pensando que el singleresult es una implementación errada porque las base de datos no las controlamos los programadores, y siempre existe y existirá un DBA que nos la puede joder…jaja.
Saludos
Interesante . Justo yo tenía un DAO que realizaba el tema de la autenticación de usuarios y utilizaba getSingleResult(), y me topé con esta excepción cuando el usuario no estaba registrado. Actualmente estaba controlando la excepción mediante try catch ,pero viendo tu código, creo que es más efectivo seguir utilizando getResultList .
Saludos desde Perú
Diego
Si definitivamente ese fue mi dolor de cabeza, realmente si no estamos seguros que el registro esté en la BD lo más seguro y efectivo es usar ResultList, justo cuando a mi me pasó me topé con el blog del cual tomé para hacer el mío, para nunca olvidarme…jaja
Saludos