web-dev-qa-db-fra.com

JPA getSingleResult () ou null

J'ai une méthode insertOrUpdate qui insère une Entity quand elle n'existe pas ou la met à jour si c'est le cas. Pour l'activer, je dois findByIdAndForeignKey, s'il a renvoyé null insert sinon sinon, mettez à jour. Le problème est comment puis-je vérifier s'il existe? Alors j'ai essayé getSingleResult. Mais il jette une exception si le

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

mais getSingleResult jette une Exception

Merci

105
Eugene Ramirez

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

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

223
cletus

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

Voici une bonne option pour cela:

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

    return list.get(0);
}
22
Rodrigo IronMan

Spring a une méthode d’utilité pour cela:

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

Essayez ceci en Java 8:

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

Si vous souhaitez utiliser le mécanisme try/catch pour traiter ce problème, vous pouvez l'utiliser comme si/else. J'ai utilisé try/catch pour ajouter un nouvel enregistrement alors que je n'en trouvais pas.

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

Voici une version typée/générique, basée sur l'implémentation de Rodrigo IronMan:

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

J'ai fait (en Java 8):

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

Alors ne fais pas ça!

Vous avez deux options:

  1. Exécutez une sélection pour obtenir le COUNT de votre jeu de résultats et n'extrayez les données que si ce nombre est différent de zéro; ou

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

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

4
Carl Smotricz

Il y a une alternative que je recommanderais: 

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

Cela protège contre l'exception Null Pointer Exception et garantit qu'un seul résultat est renvoyé.

4
aces.

La méthode non documentée uniqueResultOptional dans org.hibernate.query.Query devrait faire l'affaire. Au lieu d'avoir à attraper une NoResultException, vous pouvez simplement appeler query.uniqueResultOptional().orElse(null).

3
at_sof

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 <T> result record type
 * @return record or null
 */
public static <T> T uniqueResult(@NotNull TypedQuery<T> query) {
    List<T> results = query.setMaxResults(2).getResultList();
    if (results.size() > 1) throw new NonUniqueResultException();
    return results.isEmpty() ? null : results.get(0);
}
2
Peter Walser

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

Voici la même logique que celle suggérée par d’autres (obtenez la liste de résultats, renvoyez son seul élément ou null), à l’aide de Google Guava et d’une TypedQuery.

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

Notez que Guava renverra une exception IllegalArgumentException non intuitive si le jeu de résultats contient plusieurs résultats. (L'exception a du sens pour les clients de getOnlyElement (), car elle prend comme argument la liste des résultats, mais est moins compréhensible pour les clients de getSingleResultOrNull ().)

1
tpdi

Voici une autre extension, cette fois en Scala.

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

Avec ce souteneur:

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)
}
1
Pete Montgomery

à partir de JPA 2.2 , au lieu de .getResultList() et en vérifiant si la liste est vide ou en créant un flux, vous pouvez rétablir le flux et prendre le premier élément.

.getResultStream()
.findFirst()
.orElse(null);
0
Serafins

J'ai atteint cet objectif en obtenant une liste de résultats puis en vérifiant si elle est vide

public boolean exist(String value) {
        List<Object> options = getEntityManager().createNamedQuery("AppUsers.findByEmail").setParameter('email', value).getResultList();
        return !options.isEmpty();
    }

Il est tellement agaçant que getSingleResult() lève des exceptions

Jette:

  1. NoResultException - s'il n'y a pas de résultat
  2. NonUniqueResultException - si plus d'un résultat et une autre exception pour laquelle vous pouvez obtenir plus d'informations à partir de leur documentation
0
Uchephilz

Donc, toute la solution "essayer de réécrire sans exception" de cette page a un problème mineur. Soit son exception NonUnique ne soit pas lancée, soit jeté dans des cas erronés aussi (voir ci-dessous).

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

public static <L> L getSingleResultOrNull(TypedQuery<L> query) {
    List<L> 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, renvoyant non unique s'il y a différents éléments dans la liste, mais ne retournant pas non unique lorsque l'un de vos sélections n'est pas correctement conçu et renvoie le même objet plusieurs fois.

N'hésitez pas à commenter.

0
tg44