JPA getSingleResult () o nulo

Tengo un método insertOrUpdate que inserta una Entity cuando no existe o la actualiza si lo hace. Para habilitar esto, tengo que findByIdAndForeignKey , si devuelve null insert if not then update. El problema es ¿cómo puedo verificar si existe? Así que probé getSingleResult . Pero arroja una excepción si el

 public Profile findByUserNameAndPropertyName(String userName, String propertyName) { String namedQuery = Profile.class.getSimpleName() + ".findByUserNameAndPropertyName"; Query query = entityManager.createNamedQuery(namedQuery); query.setParameter("name", userName); query.setParameter("propName", propertyName); Object result = query.getSingleResult(); if (result == null) return null; return (Profile) result; } 

pero getSingleResult arroja una Exception .

Gracias

Lanzar una excepción es cómo getSingleResult() indica que no se puede encontrar. Personalmente no soporto este tipo de API. Fuerza el manejo espurio de excepciones sin ningún beneficio real. Solo tiene que ajustar el código en un bloque try-catch.

Alternativamente, puede consultar una lista y ver si está vacía. Eso no arroja una excepción. En realidad, dado que no está haciendo una búsqueda de clave primaria técnicamente, podría haber resultados múltiples (incluso si uno, ambos o la combinación de sus claves foráneas o restricciones lo hacen imposible en la práctica), entonces esta es probablemente la solución más apropiada.

Encapsulé la lógica en el siguiente método de ayuda.

 public class JpaResultHelper { public static Object getSingleResultOrNull(Query query){ List results = query.getResultList(); if (results.isEmpty()) return null; else if (results.size() == 1) return results.get(0); throw new NonUniqueResultException(); } } 

Aquí hay una buena opción para hacer esto:

 public static  T getSingleResult(TypedQuery query) { query.setMaxResults(1); List list = query.getResultList(); if (list == null || list.isEmpty()) { return null; } return list.get(0); } 

Spring tiene un método de utilidad para esto:

 TypedQuery query = em.createNamedQuery(namedQuery, Profile.class); ... return org.springframework.dao.support.DataAccessUtils.singleResult(query.getResultList()); 

Prueba esto en Java 8:

 Optional first = query.getResultList().stream().findFirst(); 

Si desea utilizar el mecanismo de prueba / captura para manejar este problema … entonces puede usarse para actuar como si / else. Usé el try / catch para agregar un nuevo registro cuando no encontré uno existente.

 try { //if part record = query.getSingleResult(); //use the record from the fetched result. } catch(NoResultException e){ //else part //create a new record. record = new Record(); //......... entityManager.persist(record); } 

Aquí hay una versión genérica / mecanografiada, basada en la implementación de Rodrigo IronMan:

  public static  T getSingleResultOrNull(TypedQuery query) { query.setMaxResults(1); List list = query.getResultList(); if (list.isEmpty()) { return null; } return list.get(0); } 

Hay una alternativa que recomendaría:

 Query query = em.createQuery("your query"); List elementList = query.getResultList(); return CollectionUtils.isEmpty(elementList ) ? null : elementList.get(0); 

Esto protege contra la excepción del puntero nulo, garantiza que solo se devuelve 1 resultado.

¡Entonces no hagas eso!

Tienes dos opciones:

  1. Ejecute una selección para obtener el COUNT de su conjunto de resultados, y solo obtenga los datos si este recuento no es cero; o

  2. Utilice el otro tipo de consulta (que obtiene un conjunto de resultados) y verifique si tiene 0 o más resultados. Debería tener 1, así que sáquelo de su colección de resultados y listo.

Me gustaría ir con la segunda sugerencia, de acuerdo con Cletus. Da un mejor rendimiento que (potencialmente) 2 consultas. También menos trabajo.

El método indocumentado uniqueResultOptional en org.hibernate.query.Query debería ser el truco. En lugar de tener que atrapar una NoResultException , puede llamar a query.uniqueResultOptional().orElse(null) .

Combinando los bits útiles de las respuestas existentes (limitando el número de resultados, verificando que el resultado sea único) y usando el nombre del método estabilshed (Hibernate), obtenemos:

 /** * Return a single instance that matches the query, or null if the query returns no results. * * @param query query (required) * @param  result record type * @return record or null */ public static  T uniqueResult(@NotNull TypedQuery query) { List results = query.setMaxResults(2).getResultList(); if (results.size() > 1) throw new NonUniqueResultException(); return results.isEmpty() ? null : results.get(0); } 

Lo resolví usando List myList = query.getResultList(); y verificar si myList.size() es igual a cero.

Lo he hecho (en Java 8):

 query.getResultList().stream().findFirst().orElse(null); 

Esta es la misma lógica que otros sugirieron (obtener la lista de resultados, devolver su único elemento o nulo), usando Google Guava y un TypedQuery.

 public static  getSingleResultOrNull(final TypedQuery query) { return Iterables.getOnlyElement(query.getResultList(), null); } 

Tenga en cuenta que Guava devolverá la no intuitiva IllegalArgumentException si el conjunto de resultados tiene más de un resultado. (La excepción tiene sentido para los clientes de getOnlyElement (), ya que toma la lista de resultados como su argumento, pero es menos comprensible para los clientes de getSingleResultOrNull ()).

Aquí hay otra extensión, esta vez en Scala.

 customerQuery.getSingleOrNone match { case Some(c) => // ... case None => // ... } 

Con este proxeneta:

 import javax.persistence.{NonUniqueResultException, TypedQuery} import scala.collection.JavaConversions._ object Implicits { class RichTypedQuery[T](q: TypedQuery[T]) { def getSingleOrNone : Option[T] = { val results = q.setMaxResults(2).getResultList if (results.isEmpty) None else if (results.size == 1) Some(results.head) else throw new NonUniqueResultException() } } implicit def query2RichQuery[T](q: TypedQuery[T]) = new RichTypedQuery[T](q) } 

Entonces, toda la solución de “intentar reescribir sin excepción” en esta página tiene un problema menor. O bien no arroja la excepción NonUnique, ni la arroja en algunos casos incorrectos también (ver a continuación).

Creo que la solución adecuada es (tal vez) esto:

 public static  L getSingleResultOrNull(TypedQuery query) { List results = query.getResultList(); L foundEntity = null; if(!results.isEmpty()) { foundEntity = results.get(0); } if(results.size() > 1) { for(L result : results) { if(result != foundEntity) { throw new NonUniqueResultException(); } } } return foundEntity; } 

Está regresando con nulo si hay 0 elementos en la lista, devolviendo nonunique si hay diferentes elementos en la lista, pero no devuelve nonunique cuando uno de los seleccionados no está diseñado correctamente y devuelve el mismo objeto más de una vez.

Siéntete libre de comentar