JPA getSingleResult () veya null


136

Ben yok zaman insertOrUpdateekler bir yöntem Entityvar ya da varsa güncelleyin. Bunu etkinleştirmek için findByIdAndForeignKey, eğer nulleklemezse, güncellemeliyim. Sorun, var olup olmadığını nasıl kontrol edebilirim? Ben de denedim getSingleResult. Ama eğer bir istisna atarsa

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

ama getSingleResultatar Exception.

Teşekkürler

Yanıtlar:


266

Bir istisna atmak, getSingleResult()bulunamadığını nasıl gösterir. Şahsen bu tür API'lere dayanamıyorum. Gerçek bir fayda sağlamak için sahte istisna işlemeyi zorlar. Kodu bir try-catch bloğuna sarmanız yeterlidir.

Alternatif olarak bir listeyi sorgulayabilir ve listenin boş olup olmadığını görebilirsiniz. Bu bir istisna oluşturmaz. Aslında teknik olarak birincil anahtar araması yapmadığınız için birden fazla sonuç olabilir (bir, her ikisi veya yabancı anahtarlarınızın veya kısıtlarınızın kombinasyonu bunu pratikte imkansız hale getirse bile), bu muhtemelen daha uygun bir çözümdür.


115
Kabul etmiyorum, getSingleResult()" Bu kaydın var olduğundan kesinlikle eminim. Olmazsa vur beni " gibi durumlarda kullanılır . nullBu yöntemi her kullandığımda test etmek istemiyorum çünkü geri vermeyeceğinden eminim . Aksi takdirde çok sayıda kazan plakası ve savunma programlamasına neden olur. Ve eğer kayıt gerçekten yoksa (varsaydığımızın tersi gibi), daha sonra birkaç satıra NoResultExceptionkıyasla çok daha iyidir NullPointerException. Tabii ki iki sürümü getSingleResult()olması harika olurdu, ama bir tane almak zorunda
kalırsam

8
@cletus Null aslında bir veritabanı için geçerli bir dönüş değeridir.
Bill Rosmus

12
@TomaszNurkiewicz bu iyi bir nokta. Ancak, bir tür "getSingleResultOrNull" olması gerektiği anlaşılıyor. Sanırım böyle bir paketleyici yaratabilirsiniz.
cbmeeks

2
Burada, istisna açısından bazı bilgiler getSingleResult () öğesinden başlatılır: Tek bir satırdaki tek bir sütunun değeri de dahil olmak üzere hemen hemen her şeyi almak için sorgular kullanılabilir. GetSingleResult () null değerini döndürürse, sorgunun herhangi bir satırla eşleşip eşleşmediğini veya sorgunun bir satırla eşleşip eşleşmediğini ancak seçilen sütunun değeri olarak null içerip içermediğini söyleyemezsiniz. from: stackoverflow.com/a/12155901/1242321
kullanıcı1242321

5
İsteğe bağlı <T> döndürmelidir. Bu, eksik değerleri belirtmenin iyi bir yoludur.
Vivek Kothari

33

Aşağıdaki yardımcı yöntemde mantığı kapsüle ettim.

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

2
Query.setMaxResults (1) öğesini çağırarak biraz daha uygun olabileceğinizi unutmayın. Ne yazık ki, Sorgu durumlu olduğundan, Query.getMaxResults () değerini yakalamak ve bir try-finally bloğunda nesneyi düzeltmek isteyeceksiniz ve Query.getFirstResult () ilginç bir şey döndürürse tamamen başarısız olur.
Patrick Linskey

projemizde böyle uyguladık. Bu uygulama ile hiç bir sorun
yaşamadım

25

Java 8'de bunu deneyin:

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

3
İsteğe bağlı ekleyerek kurtulabilirsiniz.orElse(null)
Justin Rowe

24

İşte bunu yapmak için iyi bir seçenek:

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

2
Temiz! TypedQuery<T>Yine de kabul ediyorum , bu durumda o getResultList()zaman zaten doğru olarak bir List<T>.
Rup

fetch()İşletme ile birlikte tamamen doldurulmamış olabilir. Bkz. Stackoverflow.com/a/39235828/661414
Leukipp

1
Bu çok hoş bir yaklaşım. setMaxResults()Akıcı bir arayüze sahip olduğunu unutmayın, böylece yazabilirsiniz query.setMaxResults(1).getResultList().stream().findFirst().orElse(null). Bu, Java 8+'daki en verimli çağrı şeması olmalıdır.
Dirk Hillbrecht

17

Baharın bunun için bir yardımcı yöntemi vardır :

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

15

Yaptım (Java 8'de):

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

sorgu ile ne demek istiyorsun?
Enrico Giurin

HibernateQuery mi demek istediniz? Saf JPA API'sını kullanmak istersem ne olur? Javax.persistence.Query böyle bir yöntemi yoktur
Enrico Giurin

2
@ EnricoGiurin, parçacığı düzenledim. İyi çalışmak. Try-catch ve liste yok. Boyut kontrolü. En güzel bir astar çözümü.
LovaBill

10

JPA 2.2'den , .getResultList()listenin boş olup olmadığını kontrol etmek veya bir akış oluşturmak yerine akışı döndürebilir ve ilk öğeyi alabilirsiniz.

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

7

Bu sorunu çözmek için dene / yakala mekanizmasını kullanmak istiyorsanız .. if / else gibi davranmak için kullanılabilir. Mevcut bir kayıt bulamadığımda yeni bir kayıt eklemek için try / catch kullandım.

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

6

İşte Rodrigo IronMan'ın uygulamasına dayalı yazılan / jenerikler:

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

5

Ben tavsiye ederim bir alternatif var:

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

Bu, Boş İşaretçi İstisnasına karşı koruma sağlar, yalnızca 1 sonucun döndürülmesini garanti eder.


4

Öyleyse yapma!

İki seçeneğiniz var:

  1. Sonuç kümenizin COUNT değerini elde etmek için bir seçim yapın ve yalnızca bu sayı sıfırdan farklıysa verileri çekin; veya

  2. Başka bir sorgu türü kullanın (sonuç kümesi alır) ve 0 veya daha fazla sonucu olup olmadığını kontrol edin. 1 olmalıdır, bu yüzden bunu sonuç koleksiyonunuzdan çekin ve işiniz bitti.

Cletus ile anlaşarak ikinci öneriyle devam ederdim. Potansiyel olarak 2 sorgudan daha iyi performans sağlar. Ayrıca daha az iş.


1
Seçenek 3 Deneme / yakalama NoResultException
Ced

3

Mevcut cevapların yararlı bitlerini birleştirerek (sonuç sayısını sınırlama, sonucun benzersiz olup olmadığını kontrol etme) ve estabilshed yöntem adını (Hazırda Bekletme) kullanarak şunları elde ederiz:

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

3

uniqueResultOptionalOrg.hibernate.query.Query içindeki belgelenmemiş yöntem hile yapmalıdır. Bir yakalamak zorunda yerine NoResultExceptionsadece arayabilirsiniz query.uniqueResultOptional().orElse(null).


2

Bunu sıfıra eşit olup List<?> myList = query.getResultList();olmadığını kullanarak ve çözerek myList.size()çözdüm.


1

İşte Google Guava ve TypedQuery kullanarak diğerlerinin önerdiği mantık (sonuç listesini alın, tek öğesini veya null değerini döndürün).

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

Sonuç kümesinde birden fazla sonuç varsa Guava'nın istenmeyen IllegalArgumentException özel durumunu döndüreceğini unutmayın. (İstisna, sonuç listesini argümanı olarak aldığı için getOnlyElement () istemcileri için mantıklıdır, ancak getSingleResultOrNull () istemcileri için daha az anlaşılabilir.)


1

İşte başka bir uzantı, bu sefer Scala'da.

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

Bu pezevenk ile:

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

Şu koda bakın:

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

Ne zaman findFirst()çağrılır belki bir NullPointerException atılabilir.

en iyi yaklaşım:

return query.getResultList().stream().filter(Objects::nonNull).findFirst().orElse(null);


0

Bu nedenle, bu sayfadaki "istisnasız yeniden yazmaya çalışın" çözümünün küçük bir sorunu vardır. Ne Unninique istisnası atmıyor, ne de bazı yanlış durumlarda atıyor (aşağıya bakınız).

Bence uygun çözüm (belki) budur:

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

Listede 0 öğesi varsa null ile geri dönüyor, listede farklı öğeler varsa unniique döndürüyor, ancak seçiminizden biri düzgün bir şekilde tasarlanmadığında ve aynı nesneyi bir kereden fazla döndürdüğünde unniique döndürmüyor.

Yorum yapmaktan çekinmeyin.


0

Bunu bir sonuç listesi alıp boş olup olmadığını kontrol ederek başardım

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

getSingleResult()İstisnalar atacak kadar sinir bozucu

atar:

  1. NoResultException - sonuç yoksa
  2. NonUniqueResultException - birden fazla sonuç ve belgelerinden daha fazla bilgi alabileceğiniz başka bir istisna varsa

-3

Bu benim için çalışıyor:

Optional<Object> opt = Optional.ofNullable(nativeQuery.getSingleResult());
return opt.isPresent() ? opt.get() : null;
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.