Spring Data JPA - 파라미터 바인딩, 반환 타입

2022. 7. 30. 19:51·공부/JPA
반응형

출처 : 인프런 실전! 스프링 데이터 JPA

Spring Data JPA 게시글은 대부분 인프런의 김영한님의 강의인 '실전! 스프링 데이터 JPA' 기반으로 내용을 정리했습니다.

 

파라미터 바인딩, 반환 타입

  • 위치 기반
select m from Member m where m.username = ?0
  • 이름 기반
select m from Member m where m.username = :name

위치 기반은 거의 사용하지 않는다. 코드 가독성과 유지보수를 위해 이름 기반 파라미터 바인딩을 사용하는 것이 좋다.

 

파라미터 바인딩

public interface MemberRepository extends JpaRepository<Member, Long> {

	@Query("select m from Member m where m.username = :name")
    Member findMembers(@Param("name") String username);
}

 

 

컬렉션 파라미터 바인딩

Collection 타입을 in 절을 지원한다.

  • MemberRepository.interface
@Query("select m from Member m where m.username in :names")
List<Member> findByNames(@Param("names") Collection<String> names);
  • MemberRepositoryTest.class
@Test
public void findByNames() {

    Member m1 = new Member("AAA", 10);
    Member m2 = new Member("BBB", 20);
    memberRepository.save(m1);
    memberRepository.save(m2);

    List<Member> usernameList = memberRepository.findByNames(Arrays.asList("AAA", "BBB"));
    for (Member Member : usernameList) {
        System.out.println("Member = " + Member);
    }
}
select
    member0_.member_id as member_i1_0_,
    member0_.age as age2_0_,
    member0_.team_id as team_id4_0_,
    member0_.username as username3_0_ 
from
    member member0_ 
where
    member0_.username in (
        ? , ?
    )

 


반환 타입

스프링 데이터 JPA는 유연한 반환타입을 지원한다.

  • MemberRepository.interface
List<Member> findListByUsername(String username); //컬렉션
Member findMemberByUsername(String username); // 단건
Optional<Member> findOptionalByUsername(String username); // 단건 Optional
  • MemberRepositoryTest.class
@Test
public void returnType() {

    Member m1 = new Member("AAA", 10);
    Member m2 = new Member("BBB", 20);
    memberRepository.save(m1);
    memberRepository.save(m2);

    List<Member> list = memberRepository.findListByUsername("AAA");
    System.out.println("=========================");
    Member member = memberRepository.findMemberByUsername("AAA");
    System.out.println("=========================");
    Optional<Member> optional = memberRepository.findOptionalByUsername("AAA");

}
2022-07-30 19:10:02.507 DEBUG 5423 --- [           main] org.hibernate.SQL                        : 
    select
        member0_.member_id as member_i1_0_,
        member0_.age as age2_0_,
        member0_.team_id as team_id4_0_,
        member0_.username as username3_0_ 
    from
        member member0_ 
    where
        member0_.username=?
=========================
2022-07-30 19:10:02.516 DEBUG 5423 --- [           main] org.hibernate.SQL                        : 
    select
        member0_.member_id as member_i1_0_,
        member0_.age as age2_0_,
        member0_.team_id as team_id4_0_,
        member0_.username as username3_0_ 
    from
        member member0_ 
    where
        member0_.username=?
=========================
2022-07-30 19:10:02.519 DEBUG 5423 --- [           main] org.hibernate.SQL                        : 
    select
        member0_.member_id as member_i1_0_,
        member0_.age as age2_0_,
        member0_.team_id as team_id4_0_,
        member0_.username as username3_0_ 
    from
        member member0_ 
    where
        member0_.username=?

DB에 원하는 데이터가 있을 경우 별 특이한 점 없이 반환이 잘 된다. 만약 데이터를 가져오지 못한 경우는 어떻게 될까? 또, 단건 조회를 했는데 여러 개의 데이터가 나온다면?

 

조회 결과가 생각과 다를 경우

  • List
List<Member> list = memberRepository.findListByUsername("CCC"); // CCC는 없다
System.out.println("list == null : " + list == null);
System.out.println("list.size : " + list.size());
    select
        member0_.member_id as member_i1_0_,
        member0_.age as age2_0_,
        member0_.team_id as team_id4_0_,
        member0_.username as username3_0_ 
    from
        member member0_ 
    where
        member0_.username=?
false
list.size : 0

List의 경우 결과값이 없는 경우 null이 반환되는 것이 아니라 빈 컬렉션이 반환된다. 그러므로 아래와 같은 로직을 사용하면 원하는 의도대로 작동되지 않는다.

if(list != null) {
....
}

 

  • 단건
Member member = memberRepository.findMemberByUsername("CCC");
System.out.println("member = " + member);
    select
        member0_.member_id as member_i1_0_,
        member0_.age as age2_0_,
        member0_.team_id as team_id4_0_,
        member0_.username as username3_0_ 
    from
        member member0_ 
    where
        member0_.username=?
member = null

단건 조회의 경우 데이터가 없으면 null이 반환된다. JPA의 기본 동작 방식과 약간 다른데 원래 순수 JPA에서는 단건 조회 시 데이터가 없으면 NoResultException이 터진다. 스프링 데이터 JPA는 개발자를 생각해 저 에러 부분을 try~catch로 잡아 null로 반환하도록 했다. Exception이 터지는 게 좋은 건가? 아니면 null로 반환 되는 게 좋은 건가에 대한 논쟁은 자바 8이 나오기 전에 의견이 분분했었고, 자바 8이 나온 이후부터는 그냥 Optional을 사용하면 된다.

 

  • Optinal
Optional<Member> optional = memberRepository.findOptionalByUsername("CCC");
System.out.println("optional = " + optional);
    select
        member0_.member_id as member_i1_0_,
        member0_.age as age2_0_,
        member0_.team_id as team_id4_0_,
        member0_.username as username3_0_ 
    from
        member member0_ 
    where
        member0_.username=?
optional = Optional.empty

자바 8 이후 부터는 데이터가 있거나 없을 수 있는 상황에선 Optinal을 사용하면 된다.

 

  • 단건, Optinal 일때 여러 건이 조회되는 경우
@Test
public void returnType() {

    Member m1 = new Member("AAA", 10);
    Member m2 = new Member("AAA", 20);
    memberRepository.save(m1);
    memberRepository.save(m2);

    Optional<Member> optional = memberRepository.findOptionalByUsername("AAA");
    System.out.println("optional = " + optional);
}

AAA를 조회하면 m1과 m2가 조회된다. 이렇게 단건일 때 여러 건이 조회되는 경우 javax.persistence.NonUniqueResultException 예외가 터진다. 그런데 예외 메세지를 잘 보면 제일 첫 줄에 org.springframework.dao.IncorrectResultSizeDataAccessException가 있는 걸 볼 수 있다. 원래 JPA에서 터지는 예외는 NonUniqueResultException가 맞다. 하지만 스프링 데이터 JPA를 사용하면 스프링 데이터 JPA에서 NonUniqueResultException을 한 번 감싸버린다. 이유는 JPA가 아닌 다른 몽고 DB나 다른 JDBC로 바꾸는 경우에도 똑같은 에러가 터질 수 있도록 의도한 것이다.

 

더욱 더 많은 타입을 반환할 수 있는 스프링 데이터 JPA에 대해 더 알고싶으면 아래 공식 사이트를 확인하면 된다.

 

Spring Data JPA - Reference Documentation

Example 109. Using @Transactional at query methods @Transactional(readOnly = true) interface UserRepository extends JpaRepository { List findByLastname(String lastname); @Modifying @Transactional @Query("delete from User u where u.active = false") void del

docs.spring.io

 

반응형
저작자표시 비영리 변경금지 (새창열림)
'공부/JPA' 카테고리의 다른 글
  • Spring Data JPA - 스프링 데이터 JPA 페이징과 정렬
  • Spring Data JPA - 순수 JPA 페이징과 정렬
  • Spring Data JPA - @Query, 값, DTO 조회하기
  • Spring Data JPA - @Query, 리포지토리 메서드에 쿼리 정의하기
데부한
데부한
어차피 할 거면 긍정적으로 하고 싶은 개발자
    반응형
  • 데부한
    동동이개발바닥
    데부한
  • 전체
    오늘
    어제
    • 분류 전체보기 (307)
      • 방통대 컴퓨터과학과 (27)
        • 잡담 (9)
        • 3학년1학기 (17)
      • 프로젝트 및 컨퍼런스 회고 (1)
        • 프로젝트 (4)
        • 한이음 프로젝트 (0)
        • 회고 (3)
      • 공부 (165)
        • Spring (37)
        • JPA (71)
        • 인프런 워밍업 클럽_BE (10)
        • Java (6)
        • React.js (27)
        • 넥사크로 (11)
        • 기타 (3)
      • 알고리즘 (85)
        • 알고리즘 유형 (10)
        • 알고리즘 풀이 (57)
        • SQL 풀이 (18)
      • 에러 해결 (13)
      • 잡담 (7)
        • 국비교육 (2)
        • 구매후기 (5)
        • 진짜 잡담 (0)
  • 블로그 메뉴

    • Github
    • Linkedin
    • 홈
    • 방명록
    • 글쓰기
    • 관리
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    프론트엔드
    토이프로젝트
    자바스크립트
    에러해결
    코딩테스트
    넥사크로
    SQL
    알고리즘
    springboot
    인프런
    SpringBoot를 이용한 RESTful Web Service 개발
    JPA
    방통대
    RESTful
    Java
    QueryDSL
    MSA
    oracle
    백준
    Spring
    IT
    react
    개발자
    토비의스프링부트
    스프링부트
    운영체제
    기출문제
    전자정부프레임워크
    egov
    프로그래머스
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
데부한
Spring Data JPA - 파라미터 바인딩, 반환 타입
상단으로

티스토리툴바