Home 쿼리 메서드 기능
Post
Cancel

쿼리 메서드 기능

쿼리 메서드

스프링 데이터 JPA는 이전 포스트에서 설명했던 공통 메서드에 더불어 어떠한 커스텀 메서드까지 알아서 구현해준다.

예를 들어 이름과 나이를 기준으로 회원을 조회하고 싶다?

1
2
3
4
public interface MemberRepository extends JpaRepository<Member, Long> {
	List<Member> findByUsername(Sring username);
	List<Member> findByUsernameAndGreaterThan(Sring username, int age);
}

findByUsernameAndAgeGreaterThan() 이러한 메서드를 구현하지 않고 네이밍만 하면 알아서 JPQL을 생성하고 실행해준다.

이것이 쿼리 메서드 기능이다.

쿼리 메서드 기능에는 3가지가 있다.

  • 메서드 이름으로 쿼리 생성
  • 메서드 이름으로 JPA NamedQuery 호출
  • @Query 애노테이션을 사용해 리포지토리 인터페이스에 쿼리를 직접 정의하는 방식

메서드 이름으로 쿼리 생성

메서드 이름을 분석해서 JPQL 쿼리를 알아서 실행해주는 방법이다.

순수 JPA로 아래와 같은 코드를 짰다고 가정하자.

1
2
3
4
5
6
public List<Member> findByUsernameAndAgeGreaterThan(String username, int age) {
	return em.createQuery("select m from Member m where m.username = :username and m.age > :age")
			.setParameter("username", username)
			.setParameter("age", age)
			.getResultList();
}

이러한 JPA 코드를 스프링 데이터 JPA를 이용한다면?

1
2
3
public interface MemberRepository extends JpaRepository<Member, Long> {
	List<Member> findByUsernameAndAgeGreaterThan(String username, int age);
}

이렇게 선언하는 것만으로도 순수 JPA 코드와 같은 동작을 만들어낸다.

이 방법의 문제는 조건이 2개가 넘어갈 경우 계속해서 And … 이런식으로 메서드 명이 길어지는 문제가 있는데 이는 JPQL을 직접 짜는 방식으로 해결한다.

쿼리 메서드 필터 조건

필터 조건은 공식 사이트를 참고하자.

And, Or, In 등의 정보가 나타나있다. 필요 시 찾아서 사용하면 된다.

  • 조회: find…By, read…By, query…By, get…By
    • …에는 식별하기 위한 내용이 들어가도 된다.
    • findHelloBy()
    • By뒤에 where절 같은 조건이 붙으면 된다.
  • COUNT: count…By
    • 반환타입 = Long
  • EXISTS: exists…By
    • 반환타입 = boolean
  • 삭제: delete…By, remove…By
    • 반환타입 = Long
  • DISTINCT: findDistinct, findMemberDistinctBy
  • LIMIT: findFirst3, findFirst, findTop, findTop3

참고로 이 기능은 엔티티의 필드명이 변경되면 인터페이스에 정의한 메서드 이름도 반드시 함께 변경해야 한다.

그렇지 않으면 애플리케이션을 시작하는 시점에 오류가 발생할 수 있다.

이렇게 애플리케이션 로딩 시점에 오류를 인지할 수 있는 점도 스프링 데이터 JPA의 큰 장점이다.

JPA NamedQuery

거의 사용할 일이 없다.

1
2
3
@Entity 
@NamedQuery( name="Member.findByUsername", query="select m from Member m where m.username = :username") 
public class Member { ... }
1
2
@Query(name = "Member.findByUsername") 
List findByUsername(@Param("username") String username);

쿼리를 직접 정의하고 싶은 경우에는 아래의 @Query 기능을 사용하기 때문이다.

@Query - 리포지토리 메서드에 쿼리 직접 정의

1
2
3
4
public interface MemberRepository extends JpaRepository<Member, Long> {
	@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);
}
  • @org.springframework.data.jpa.repository.Query 애노테이션을 사용
  • 실행할 메서드에 정적 쿼리를 직접 작성한다.
  • JPA Named 쿼리처럼 애플리케이션 실행 시점에 문법 오류를 발견할 수 있다.
    • 매우 큰 장점이다.

실무에서 메서드 이름으로 쿼리 생성 기능보다 @Query 기능을 자주 사용하게 된다.

위에서 말했던 단점인 파라미터가 증가했을 때 메서드 이름이 지저분해지는 단점 때문이다.

@Query - 값, DTO 조회

  • 단순 값 하나 조회
1
2
@Query("select m.username from Member m")  
List<String> findUsernameList();

JPA 값 타입(@Embedded)도 이러한 방식으로 조회가 가능하다.

  • DTO로 직접 조회
1
2
3
4
5
6
7
8
9
10
11
12
13
@Data  
public class MemberDto {  
  
	private Long id;  
	private String username;  
	private String teamName;  
	  
	public MemberDto(Long id, String username, String teamName) {  
		this.id = id;  
		this.username = username;  
		this.teamName = teamName;  
	}  
}
1
2
@Query("select new study.datajpa.dto.MemberDto(m.id, m.username, t.name) from Member m join m.team t")  
List<MemberDto> findMemberDto();

파라미터 바인딩

  • 위치 기반
  • 이름 기반

2가지 파라미터 바인딩이 존재한다.

1
2
select m from Member m where m.username = ?0 //위치기반
select m from Member m where m.username = :name //이름기반
1
2
@Query("select m from Member m where m.username = :username)
Member findMember(@param("name") String username);

코드 가독성과 유지보수를 위해 이름 기반 파라미터 바인딩을 권장한다.

컬렉션 파라미터 바인딩

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

1
2
@Query("select m from Member m where m.username in :names)
List<Member> findByNames(@Param("names") List<String> names);

반환 타입

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

1
2
3
List<Member> findByUsername(String name); //컬렉션
Member findByUsername(String name); //단건
Optional<Member> findByUsername(String name); //단건 Optional

조회 결과가 많거나 없다면?

  • 컬렉션
    • 결과 없음 = 빈 컬렉션 반환
  • 단건 조회
    • 결과 없음 = null 반환
    • 결과가 2건 이상 = NonUniqueResultException 발생

참고: 단건 조회 시 스프링 데이터 JPA는 내부에서 JPQL의 Query.getSingleResult() 메서드를 호출한다.

이 메서드를 호출했을 때 조회결과가 없으면 NoResultException이 발생하는데 이는 개발자가 다루기 불편한 점이다.

스프링 데이터 JPA는 단건 조회 시 이 예외가 발생하면 예외를 무시하고 null을 반환한다.

This post is licensed under CC BY 4.0 by the author.