JPA getSingleResult () ou null

J’ai une méthode insertOrUpdate qui insère une Entity lorsqu’elle n’existe pas ou la met à jour si c’est le cas. Pour activer ceci, je dois findByIdAndForeignKey , s’il a renvoyé un insert null s’il n’a pas été mis à jour. Le problème est comment vérifier si elle existe? J’ai donc essayé getSingleResult . Mais il jette une exception si le

 public Profile findByUserNameAndPropertyName(Ssortingng userName, Ssortingng propertyName) { Ssortingng 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; } 

mais getSingleResult lève une Exception .

Merci

Lancer une exception, c’est comment getSingleResult() indique qu’il est introuvable. Personnellement, je ne supporte pas ce type d’API. Il force la manipulation des exceptions fausses sans réel bénéfice. Vous devez juste envelopper le code dans un bloc try-catch.

Sinon, vous pouvez demander une liste et voir si elle est vide. Cela ne jette pas d’exception. En fait, comme vous ne faites pas techniquement une recherche de clé primaire, il peut y avoir plusieurs résultats (même si l’un, les deux ou la combinaison de vos clés ou contraintes étrangères rendent cela impossible en pratique), c’est probablement la solution la plus appropriée.

J’ai encapsulé la logique dans la méthode d’assistance suivante.

 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(); } } 

Voici une bonne option pour ce faire:

 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 a une méthode utilitaire pour cela:

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

Essayez ceci dans Java 8:

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

Si vous souhaitez utiliser le mécanisme try / catch pour gérer ce problème, alors il peut être utilisé comme si / autrement. J’ai utilisé le try / catch pour append un nouvel enregistrement alors que je n’en trouvais aucun.

 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); } 

Voici une version typée / générique, basée sur l’implémentation 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); } 

Il y a une alternative que je recommanderais:

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

Cela protège contre les exceptions de pointeur nul, garantit que seul un résultat est retourné.

Alors ne fais pas ça!

Vous avez deux options:

  1. Exécuter une sélection pour obtenir le COUNT de votre jeu de résultats et extraire les données uniquement si ce nombre est différent de zéro; ou

  2. Utilisez l’autre type de requête (qui obtient un jeu de résultats) et vérifiez s’il contient 0 ou plus de résultats. Il devrait avoir 1, alors retirez-le de votre collection de résultats et vous avez terminé.

J’allais avec la deuxième suggestion, en accord avec Cletus. Il donne de meilleures performances que (potentiellement) 2 requêtes. Aussi moins de travail.

La méthode non documentée uniqueResultOptional dans org.hibernate.query.Query devrait faire l’affaire. Au lieu de devoir intercepter une NoResultException vous pouvez simplement appeler query.uniqueResultOptional().orElse(null) .

En combinant les bits utiles des réponses existantes (en limitant le nombre de résultats, en vérifiant que le résultat est unique) et en utilisant le nom de méthode estabilshed (Hibernate), on obtient:

 /** * 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); } 

J’ai résolu ce problème en utilisant List myList = query.getResultList(); et vérifier si myList.size() est égal à zéro.

J’ai fait (en Java 8):

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

Voici la même logique que les autres ont suggérée (obtenir le resultList, renvoyer son seul élément ou null), en utilisant Google Guava et un TypedQuery.

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

Notez que Guava renverra l’IlllegalArgumentException non intuitive si le jeu de résultats comporte plusieurs résultats. (L’exception est logique pour les clients de getOnlyElement (), car elle prend l’argument de la liste de résultats, mais est moins compréhensible pour les clients de getSingleResultOrNull ().)

Voici une autre extension, cette fois à Scala.

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

Avec ce proxénète:

 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) } 

Donc, toute la solution “essayer de réécrire sans exception” dans cette page a un problème mineur. Soit son exception NonUnique ne lançant pas, ni le lancer dans de mauvais cas aussi (voir ci-dessous).

Je pense que la solution appropriée est (peut-être) ceci:

 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; } 

Son retour avec null s’il y a 0 élément dans la liste, retournant nonunique s’il y a différents éléments dans la liste, mais ne retournant pas nonunique quand l’un de vos select n’est pas correctement conçu et retourne le même object plus d’une fois.

N’hésitez pas à commenter.