본문 바로가기
JAVA/JPA

[JPA] getSingleResult() 와 null

by 민트맛녹차 2022. 3. 22.

토이프로젝트 중 발생한 에러에 대해 원인을 찾고 정리하였다.

당시 상황은 다음과 같다.

public Optional<User> findByEmail(String email) {
    return Optional.ofNullable(
            em.createQuery("select u from User u where u.email = :email", User.class)
                    .setParameter("email", email)
                    .getSingleResult());
}

UserRepository에 findByEmail 메서드를 생성하고 쿼리문의 반환값이 null일수도 있다 생각하여 Optional을 통해 wrapping 했다.

private void validateDuplicateUserByEmail(User user) {
    Optional<User> findUser = userRepository.findByEmail(user.getEmail());
    if (findUser.isPresent()) {
        throw new IllegalStateException("이미 존재하는 회원입니다.");
    }
}

UserService에 회원가입 메서드에 필요한 중복 검증 메서드를 생성하였다. 중복 검증 메서드는 입력받은 이메일을 가진 회원이 있다면 Exception을 반환하게 설계되었다. UserService의 Test 코드를 입력한 후 실행해보니 
No entity found for query; nested exception is javax.persistence.NoResultException: No entity found for query

라는 오류가 떴고 findByEmail에서 오류가 생성됨을 확인했다. 즉, findByEmail에서 생성한 쿼리에 해당하는 엔티티가 없다는 뜻이다.
오류의 원인을 찾던 도중 나와 같은 경험을 한 블로그를 찾았다.

오류의 원인은 컬럼이 없는 테이블에 쿼리문을 날려 Exception이 생기는 것이다. 해당 상황에서 getSingleResult()가 null값을 반환할 줄 알았지만  getSingleResult()는 null을 반환하지 않고 Exception을 반환한다. 

public Optional<User> findByEmail(String email) {
    List<User> findUser = em.createQuery("select u from User u where u.email = :email", User.class)
            .setParameter("email", email)
            .getResultList();
    return findUser.stream().findAny();
}

따라서 findByEmail 메서드를 위와 같이 리팩토링했다. getResultList()에는 null이 담기고, stream().findAny()를 통해 Optional을 wrapping 했다. 리팩토링 후 UserService의 test는 다행히도 성공적이었다.

 

 

 

참조
https://lktgt.tistory.com/34

 

'JAVA > JPA' 카테고리의 다른 글

[JPA] Soft Delete 적용과 @Where, @SQLDelete  (0) 2023.09.06
[JPA] DirtyCheck vs merge  (0) 2022.05.15
[JPA] CascadeType.REMOVE vs orphanRemoval = true  (0) 2022.03.19

댓글