JPA getSingleResult () 또는 널


136

존재하지 않는 경우 insertOrUpdate삽입 Entity하거나 존재하는 경우 업데이트 하는 메소드 가 있습니다. 이를 사용하려면, 나는에 가지고 findByIdAndForeignKey가 반환하는 경우, null삽입하지 않을 경우 다음 업데이트를. 문제는 그것이 존재하는지 어떻게 확인합니까? 그래서 나는 노력했다 getSingleResult. 그러나 예외가 발생하면

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

하지만 getSingleResult던졌습니다 Exception.

감사

답변:


266

예외를 던지면 getSingleResult()찾을 수 없다는 것을 나타냅니다. 개인적으로 나는 이런 종류의 API를 견딜 수 없습니다. 실질적인 이점이 없도록 허위 예외 처리를 강요합니다. try-catch 블록으로 코드를 감싸면됩니다.

또는 목록을 쿼리하여 비어 있는지 확인할 수 있습니다. 예외는 아닙니다. 실제로 기술적으로 기본 키 조회를 수행하지 않기 때문에 여러 결과가있을 수 있습니다 (외래 키 또는 제약 조건 중 하나 또는 둘 다 또는 조합으로 실제로 불가능한 경우에도). 이것은 아마도 더 적절한 해결책 일 것입니다.


115
동의하지 않으며 다음 getSingleResult()과 같은 상황에서 사용됩니다 : " 나는이 기록이 존재한다고 확신한다. 그렇지 않으면 나를 쏴라 ." 나는에 대한 테스트를하지 않으려는 null내가 때문에이 방법을 사용할 때마다 반드시 그것을 반환하지 않습니다. 그렇지 않으면 많은 상용구와 방어 프로그래밍이 발생합니다. 그리고 레코드가 실제로 존재하지 않으면 (우리가 가정 한 것과는 반대로) 나중에 몇 줄 을 NoResultException비교 하는 것이 훨씬 좋습니다 NullPointerException. 물론 두 가지 버전을 갖는 getSingleResult()것은 굉장하지만, 하나를 선택해야한다면 ...
Tomasz Nurkiewicz

8
@cletus Null은 실제로 데이터베이스에 대한 유효한 반환 값입니다.
Bill Rosmus 2016 년

12
@TomaszNurkiewicz 좋은 지적입니다. 그러나 "getSingleResultOrNull"유형이 있어야합니다. 그런 래퍼를 만들 수 있다고 생각합니다.
cbmeeks 2016 년

2
다음은 getSingleResult ()에서 발생 된 예외 시작의 이점에 관한 정보입니다. 쿼리는 단일 행의 단일 열 값을 포함하여 거의 모든 것을 검색하는 데 사용될 수 있습니다. getSingleResult ()가 null을 반환하면 쿼리가 행과 일치하지 않는지 또는 쿼리가 행과 일치하지만 선택한 열에 값으로 null이 포함되어 있는지 여부를 알 수 없습니다. from : stackoverflow.com/a/12155901/1242321
user1242321

5
Optional <T>를 반환해야합니다. 이는 결 측값을 나타내는 좋은 방법입니다.
Vivek Kothari

33

다음 도우미 메서드에서 논리를 캡슐화했습니다.

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)를 호출하면 조금 더 최적 일 수 있습니다. 안타깝게도 Query는 stateful이므로 Query.getMaxResults ()의 값을 캡처하고 try-finally 블록으로 객체를 수정하고 Query.getFirstResult ()가 흥미로운 것을 반환하면 완전히 실패 할 수 있습니다.
Patrick Linskey

그것이 우리 프로젝트에서 구현 한 방법입니다. 이 구현에 아무런 문제가 없었습니다
walv

25

Java 8에서 이것을 시도하십시오.

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

3
당신은 추가하여 선택적으로 제거 할 수.orElse(null)
저스틴 로우

24

이 작업을 수행하기위한 좋은 옵션이 있습니다.

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
산뜻한! TypedQuery<T>그래도 동의 하지만 getResultList()그때는 이미로 올바르게 입력되었습니다 List<T>.
Rup

fetch()엔티티 와 결합하여 완전히 채워지지 않을 수 있습니다. 참조 stackoverflow.com/a/39235828/661414
Leukipp

1
이것은 매우 좋은 접근법입니다. 주 setMaxResults()당신이 쓸 수 있도록 유창 인터페이스를 가지고 있습니다 query.setMaxResults(1).getResultList().stream().findFirst().orElse(null). 이것은 Java 8 이상에서 가장 효율적인 호출 체계 여야합니다.
Dirk Hillbrecht

17

Spring 은이를위한 유틸리티 메소드 를 가지고 있습니다 :

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

15

(Java 8에서) 완료했습니다.

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

질문의 의미는 무엇입니까?
Enrico Giurin

HibernateQuery를 의미합니까? 순수한 JPA API를 사용하려면 어떻게해야합니까? 들은 javax.persistence.Query에서 이러한 방법이 없습니다
엔리코 Giurin

2
@ EnricoGiurin, 나는 스 니펫을 편집했습니다. 잘 작동합니다. try-catch 및 list.size 확인이 없습니다. 가장 좋은 라이너 솔루션.
LovaBill

10

JPA 2.2 에서 .getResultList()list가 비어 있는지 확인하거나 스트림을 만드는 대신 스트림을 반환하고 첫 번째 요소를 사용할 수 있습니다.

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

7

try / catch 메커니즘을 사용하여이 문제를 처리하려는 경우 if / else처럼 작동하는 데 사용할 수 있습니다. 기존 레코드를 찾지 못한 경우 try / catch를 사용하여 새 레코드를 추가했습니다.

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

다음은 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);
}

5

내가 추천하는 대안이 있습니다.

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

이것은 Null Pointer Exception을 방지하여 1 개의 결과 만 반환되도록합니다.


4

그러지 마!

두 가지 옵션이 있습니다.

  1. 선택을 실행하여 결과 집합의 COUNT를 얻고이 수가 0이 아닌 경우에만 데이터를 가져옵니다. 또는

  2. 다른 종류의 쿼리 (결과 집합을 가져옴)를 사용하고 0 개 이상의 결과가 있는지 확인하십시오. 1이 있어야하므로 결과 컬렉션에서 꺼내면 완료됩니다.

나는 Cletus와 동의하여 두 번째 제안을 할 것입니다. (잠재적으로) 2 개의 쿼리보다 성능이 좋습니다. 또한 일이 적습니다.


1
옵션 3 시도 / 잡기 NoResultException
Ced

3

기존 답변의 유용한 비트 (결과 수 제한, 결과가 고유한지 확인)와 estabilshed 메소드 이름 (Hibernate)을 사용하면 다음과 같은 결과를 얻을 수 있습니다.

/**
 * 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 의 문서화되지 않은 메소드 가 트릭을 수행해야합니다. 를 잡는 대신 NoResultException전화하면 query.uniqueResultOptional().orElse(null)됩니다.


2

나는 0 List<?> myList = query.getResultList();myList.size()같은지 확인 하고 사용하여 이것을 해결했다 .


1

다음은 Google Guava와 TypedQuery를 사용하여 다른 사람들이 제안한 것과 동일한 논리입니다 (resultList 가져 오기, 유일한 요소 또는 null 반환).

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

결과 집합에 둘 이상의 결과가있는 경우 Guava는 직관적이지 않은 IllegalArgumentException을 반환합니다. 결과 목록을 인수로 사용하지만 getSingleResultOrNull ()의 클라이언트에게는 이해하기 어렵 기 때문에 getOnlyElement ()의 클라이언트에게는 예외가 적합합니다.


1

이번에는 스칼라에 또 다른 확장이 있습니다.

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

이 포주로 :

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

이 코드를보십시오 :

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

findFirst()호출 되면 NullPointerException이 발생할 수 있습니다.

가장 좋은 방법은 다음과 같습니다.

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


0

따라서이 페이지의 모든 "예외없이 다시 작성"솔루션에는 사소한 문제가 있습니다. NonUnique 예외가 발생하지 않거나 잘못된 경우에도 발생합니다 (아래 참조).

적절한 해결책은 다음과 같습니다.

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

목록에 0 요소가 있으면 null로 반환되고 목록에 다른 요소가 있으면 비 고유를 반환하지만 선택 중 하나가 제대로 설계되지 않았고 동일한 객체를 여러 번 반환하면 비 고유를 반환하지 않습니다.

의견을 주시기 바랍니다.


0

결과 목록을 얻은 다음 비어 있는지 확인 하여이 작업을 수행했습니다.

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

getSingleResult()예외가 발생 하는 것은 매우 성가신 일입니다.

던졌습니다 :

  1. NoResultException-결과가없는 경우
  2. NonUniqueResultException-하나 이상의 결과 및 문서에서 더 많은 정보를 얻을 수있는 다른 예외가있는 경우

-3

그것은 저에게 효과적입니다.

Optional<Object> opt = Optional.ofNullable(nativeQuery.getSingleResult());
return opt.isPresent() ? opt.get() : null;
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.