토이프로젝트 중 발생한 에러에 대해 원인을 찾고 정리하였다.
당시 상황은 다음과 같다.
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 |
댓글