실전! 스프링 데이터 JPA - 인프런 | 강의
스프링 데이터 JPA는 기존의 한계를 넘어 마치 마법처럼 리포지토리에 구현 클래스 없이 인터페이스만으로 개발을 완료할 수 있습니다. 그리고 반복 개발해온 기본 CRUD 기능도 모두 제공합니다.
www.inflearn.com
스프링 데이터 JPA는 메소드 이름만으로 쿼리를 자동으로 생성해주는 쿼리 메소드(Query Method) 기능을 제공합니다. 이를 사용하면 별도의 쿼리문 작성 없이도 간단하게 데이터를 조회할 수 있습니다. 쿼리 메소드 기능은 다양한 형태의 메소드를 지원하며, 이번 글에서는 몇 가지 예시를 살펴보겠습니다.
메소드 이름으로 쿼리 생성
메소드의 이름을 분석해서 JPQL 쿼리를 실행시켜 준다.
이름과 나이를 기준으로 회원을 조회하는 기능으로 살펴 보자.
스프링 데이터 JPA 예시
public interface MemberRepository extends JpaRepository<Member, Long> {
List<Member> findByUsernameAndAgeGreaterThan(String username, int age);
}
쿼리 메소드 이름 규칙
스프링 데이터 JPA의 쿼리 메소드 이름 규칙은 공식 문서에서 확인 할 수 있습니다.
해당 기능은 엔티티의 필드명이 변경되면 인터페이스에 정의한 메서드 이름도 꼭 함께 변경해야 한다. 그렇지 않으면 애플리케이션을 시작하는 시점에 오류가 발생한다.
이렇게 애플리케이션 로딩 시점에 오류을 인지할 수 있는 것이 스프링 데이터 JPA의 매우 큰 장점이다.
JPA NamedQuery
- Entity에 쿼리를 정의해서 해당 쿼리에 정의된 이름으로 호출하는 방법
- 스프링 데이터 JPA를 주로 사용하는 실무에서는 잘 사용하지 않고, 후술할
@Query
방법을 주로 사용한다.
- Entity에
@NamedQuery
정의
/* Entity Area */
@Entity
@NamedQuery(name="Member.findByUsername",
query="select m from Member m where m.username = :username"
)
public class Member {
...
}
- 순수 JPA를 이용한 NamedQuery 호출
public class MemberRepository {
public List<Member> findByUsername(String username) {
...
List<Member> resultList = em.createNamedQuery("Member.findByUsername", Member.class)
.setParameter("username", username)
.getResultList();
}
}
- 스프링 데이터 JPA를 이용한 NamedQuery 사용
@Query(name = "Member.findByUsername")
List<Member> findByUsername(@Param("username") String username);
→ @Query
생략가능(메서드 이름으로 먼저 조회를 하기 때문)
@Query, 리포지토리 메소드에 쿼리 정의하기
메서드에 JPQL 쿼리를 작성할 수 있다.
@Query("select m from Member m where m.username= :username and m.age = :age" )
List<Member> findUser(@Param("username") String username, @Param("age") int age);
- @Query 어노테이션 사용
- 실행할 메서드에 정적 쿼리를 직접 작성하기에 익명 NamedQuery라 할 수 있다.
- JPA Named 쿼리처럼 애플리케이션 실행 시점에 문법 오류를 발견할 수 있다.
실무에서는 쿼리 메서드기능을 이용하여 기능 구현하는 것은 파라미터가 증가할수록 메서드명이 복잡해지므로, 해당
@Query
기능을 자주 사용한다.
값 조회하기
@Query("select m.username from Member m")
List<String> findUsernameList();
DTO로 직접 조회
@Query("select new study.datajpa.repository.MemberDto(m.id, m.username, t.name)" +
" from Member m join m.team t")
List<MemberDto> findMemberDto();
파라미터 바인딩
파라미터 바인딩은 위치기반 바인딩과 이름기 기반 바인딩 두가지가 존재한다.
select m from Member m where m.username = ?0 //위치 기반
select m from Member m where m.username = :name //이름 기반
이름기반 바인딩을 사용할 것!
- 위치기반을 사용할 경우, 어떤 파라미터가 매핑되는지 가독성이 떨어지고, 순서가 바뀔경우 에러가 날 확률이 높다.
컬렉션 파라미터 바인딩
Collection 타입으로 in절 지원
@Query("select m from Member m where m.username in :names")
List<Member> findByNames(@Param("names") List<String> names);
반환 타입
List<Member> findByUsername(String name); //컬렉션
Member findByUsername(String name); //단건
Optional<Member> findByUsername(String name); //단건 Optional
조회 결과가 없는 경우
- 컬렉션 : 빈 컬렉션 반환
- 단건 → null 반환
결과가 2건 이상일 경우
- 단건 →
javax.persistence.NonUniqueResultException
예외 발생
정리
기본적으로 메소드 이름으로 생성해주는 기능을 사용하게 되며, 쿼리 파라미터가 많아져서 메소드 명이 길어질 경우, @Query
를 이용해서 직접 쿼리를 작성해서 사용하면 된다.