스프링 데이터 JPA는 유연한 반환 타입을 지원합니다.
컬렉션, 단건, 옵셔널 모두 지원합니다.
List<Member> findListByUsername(String name); //컬렉션
Member findMemberByUsername(String name); //단건
Optional<Member> findOptionalByUsername(String name); //단건 Optional
만약 findListByUsername() 이 한 건이라는게 보장되면 List<Member> 가 아니라 Member 로 받아도 됩니다.
마찬가지로 findMemberByUsername() 도 List<Member> 로 받을 수 있는 것이죠.
@Test
void returnType() {
Member m1 = new Member("AAA", 10);
Member m2 = new Member("BBB", 20);
memberRepository.save(m1);
memberRepository.save(m2);
List<Member> result1 = memberRepository.findListByUsername("AAA");
Member result2 = memberRepository.findMemberByUsername("AAA");
Optional<Member> result3 = memberRepository.findOptionalByUsername("AAA");
assertThat(result1).hasSize(1)
.extracting("username", "age")
.containsExactlyInAnyOrder(
tuple("AAA", 10)
);
assertThat(result2)
.extracting("username", "age")
.contains("AAA", 10);
assertThat(result3.get())
.extracting("username", "age")
.contains("AAA", 10);
}
결과 없음 → null 반환
<aside> ❗ 스프링 데이터 JPA 와 JPA 의 단건 조회 차이
스프링 데이터 JPA 는 결과가 없다면 null을 반환합니다.
하지만 JPA 는 예외가 발생합니다.
JPA 에서 단건으로 지정한 메서드를 호출하면 내부에서 JPQL의 Query.getSingleResult() 메서드를 호출합니다. 이 메서드를 호출했을 때 조회 결과가 없으면 javax.persistence.NoResultException 예외가 발생하는데 개발자 입장에서 다루기가 상당히 불편합니다.
그래서 스프링 데이터 JPA는 단건을 조회할 때 이 예외가 발생하면 예외를 무시하고(내부적으로 try-catch) 대신에 null 을 반환하는 것이죠.
참고로 DB에서 조회했는데 데이터가 있는지 여부가 불확실하다면 그냥 Optional 을 사용하는게 맞습니다.
</aside>
결과가 2건 이상: javax.persistence.NonUniqueResultException 예외 발생
원래라면 NonUniqueResultException 가 발생해야 하는데 IncorrectResultSizeDataAccessException 예외가 발생합니다.
리포지토리의 기술은 JPA 가 될 수도 있고 아니면 MongoDB 같은 다른 기술이 될 수 있습니다.
그래서 스프링은 JPA 에 의존하는 게 아니라 스프링이 추상화한 예외에 의존하도록 예외를 변환해서 반응을 해주는 겁니다.
즉, NonUniqueResultException 예외를 스프링이 IncorrectResultSizeDataAccessException 로 바꿔주는 메커니즘을 제공합니다.
<aside> ❗ 추가적인 반환 타입 공식문서
https://docs.spring.io/spring-data/jpa/reference/repositories/query-return-types-reference.html#appendix.query.return.types
</aside>