[JPA] 쿼리 메소드 기능(@NamedQuery, @Query, 파라미터 바인딩, 반환 타입)

컴공생의 코딩 일기·2023년 2월 1일
0

JPA

목록 보기
8/14
post-thumbnail
post-custom-banner

쿼리 메소드 기능

  • 메소드 이름으로 쿼리 생성
  • NamedQuery
  • @Query Repository 메소드에 쿼리 정의
  • 파라미터 바인딩
  • 페이징과 정렬
  • 벌크성 수정 쿼리
  • @EntityGraph

쿼리 메소드 기능 3가지

  • 메소드 이름으로 쿼리 생성
  • 메소드 이름으로 JPA NamedQuery 호출
  • @Query 어노테이션을 사용해서 Repository 인터페이스 쿼리 직접 정의

메소드 이름으로 쿼리 생성

  • 메소드 이름을 분석해서 JPQL 쿼리를 실행한다.
public interface MemberRepository extends JpaRepository<Member, Long> {

// 이름과 나이를 기준으로 회원을 조회
    List<Member> findByUsernameAndAgeGreaterThan(String username, int age);

}

쿼리 메소드 필터 조건

KeywordSampleJPQL snippet
DistinctfindDistinctByLastnameAndFirstnameselect distinct … where x.lastname = ?1 and x.firstname = ?2
AndfindByLastnameAndFirstname… where x.lastname = ?1 and x.firstname = ?2
OrfindByLastnameOrFirstname… where x.lastname = ?1 or x.firstname = ?2
Is, EqualsfindByFirstname,findByFirstnameIs,findByFirstnameEquals… where x.firstname = ?1
BetweenfindByStartDateBetween… where x.startDate between ?1 and ?2
LessThanfindByAgeLessThan… where x.age < ?1
LessThanEqualfindByAgeLessThanEqual… where x.age <= ?1
GreaterThanfindByAgeGreaterThan… where x.age > ?1
GreaterThanEqualfindByAgeGreaterThanEqual… where x.age >= ?1
AfterfindByStartDateAfter… where x.startDate > ?1
BeforefindByStartDateBefore… where x.startDate < ?1
IsNull, NullfindByAge(Is)Null… where x.age is null
IsNotNull, NotNullfindByAge(Is)NotNull… where x.age not null
LikefindByFirstnameLike… where x.firstname like ?1
NotLikefindByFirstnameNotLike… where x.firstname not like ?1
StartingWithfindByFirstnameStartingWith… where x.firstname like ?1 (parameter bound with appended %)
EndingWithfindByFirstnameEndingWith… where x.firstname like ?1 (parameter bound with prepended %)
ContainingfindByFirstnameContaining… where x.firstname like ?1 (parameter bound wrapped in %)
OrderByfindByAgeOrderByLastnameDesc… where x.age = ?1 order by x.lastname desc
NotfindByLastnameNot… where x.lastname <> ?1
InfindByAgeIn(Collection<Age> ages)… where x.age in ?1
NotInfindByAgeNotIn(Collection<Age> ages)… where x.age not in ?1
TruefindByActiveTrue()… where x.active = true
FalsefindByActiveFalse()… where x.active = false
IgnoreCasefindByFirstnameIgnoreCase… where UPPER(x.firstname) = UPPER(?1)

스프링 데이터 JPA가 제공하는 쿼리 메소드 기능

  • 조회: find...By, read...By, get...By
    • 예) findMemberBy: ...에 식별하기 위한 내용(설명)이 들어가도 된다.(생략 가능)
  • COUNT: count...By -> 반환 타입: long
  • EXISTS: exists...By -> 반환 타입: boolean
  • DELETE: delete...By -> 반환 타입: long
  • DISTINCT: findDistinct, findMemberDistinctBy
  • LIMIT: findFirst3, findFirst, findTop, findTop3

참고: 이 기능은 엔티티의 필드명이 변경되면 인터페이스에 정의한 메소드 이름도 반드시 함께 변경해야 한다. 그렇지 않으면 애플리케이션을 시작하는 시점에 오류가 발생한다.
이렇게 애플리케이션 로딩 시점에 오류를 인지할 수 있는 것이 스프링 데이터 JPA의 매우 큰 장점이다.

JPA NamedQuery

JPA에서는 @NamedQuery 어노테이션을 제공한다 @NamedQuery는 Entity 클래스 내에서 JPQL 쿼리를 작성할 수 있고 정적 쿼리로 애플리케이션 로딩 시점에 JPQL 문법을 체크하고 미리 파싱하기 때문에 요류를 빨리 확인 할 수 있다.

@Entity
@NamedQuery(
        name = "Member.findByUsername", 
        query = "select m from Member m where m.username = :username"
)
public class Member {
    @Id @GeneratedValue
    @Column(name = "member_id")
    private Long id;
    private String username;
    private int age;
.....
}
public interface MemberRepository extends JpaRepository<Member, Long> {
    // @Query(name = "Member.findByUsername") 생략 가능
    List<Member> findByUsername(@Param("username") String username);
}
  • 스프링 데이터 JPA는 선언한 도메인 클래스 + .(점) + 메소드 이름으로 NamedQuery 쿼리를 찾아서 실행한다. (그렇기 때문에 @Query 생략이 가능하다.)
  • 만약 실행할 Named 쿼리가 없으면 메서드 이름으로 쿼리 생성 전략을 사용한다.
  • 만약 JPQL 쿼리에 파라미터가 있을 경우 @Param 어노테이션을 사용해 파라미터를 지정해줘야 한다.

@Query

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);

    @Query("select m.username from Member m")
    List<String> findUsernameList();

    @Query("select new com.study.datajpa.dto.MemberDto(m.id, m.username, t.name) " +
            "from Member m join m.team t")
    List<MemberDto> findMemberDto();
}

@Query 어노테이션에 직접 JPQL 쿼리를 작성하면 엔티티에서 @NamedQuery 어노테이션으로 작성할 필요 없이 JpaRepository 안에서 바로 JPQL 쿼리 사용이 가능하다.

실무에서는 메소드 이름으로 쿼리 생성 기능은 파라미터가 증가하면 메서드 이름이 매우 지저분해진다. 따라서 Qeury 기능을 사용하게 된다. -스프링 데이터 JPA 김영한-

파라미터 바인딩

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") Collection<String> names);

반환 타입

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

List<Member> findListByUsername(String username); 
Member findMemberByUsername(String username);
Optional<Member> findOptionalByUsername(String username);
voidDenotes no return value.
PrimitivesJava primitives.
Wrapper typesJava wrapper types.
TA unique entity. Expects the query method to return one result at most. If no result is found, null is returned. More than one result triggers an IncorrectResultSizeDataAccessException.
Iterator<T>An Iterator.
Collection<T>A Collection.
List<T>A List.
Optional<T>A Java 8 or Guava Optional. Expects the query method to return one result at most. If no result is found, Optional.empty() or Optional.absent() is returned. More than one result triggers an IncorrectResultSizeDataAccessException.
Option<T>Either a Scala or Vavr Option type. Semantically the same behavior as Java 8’s Optional, described earlier.
Stream<T>A Java 8 Stream.
Streamable<T>A convenience extension of Iterable that directy exposes methods to stream, map and filter results, concatenate them etc.
Types that implement Streamable and take a Streamable constructor or factory method argumentTypes that expose a constructor or ….of(…)/….valueOf(…) factory method taking a Streamable as argument. See Returning Custom Streamable Wrapper Types for details.
Vavr Seq, List, Map, SetVavr collection types. See Support for Vavr Collections for details.
Future<T>A Future. Expects a method to be annotated with @Async and requires Spring’s asynchronous method execution capability to be enabled.
CompletableFuture<T>A Java 8 CompletableFuture. Expects a method to be annotated with @Async and requires Spring’s asynchronous method execution capability to be enabled.
Slice<T>A sized chunk of data with an indication of whether there is more data available. Requires a Pageable method parameter.
Page<T>A Slice with additional information, such as the total number of results. Requires a Pageable method parameter.
...............

조회 시 결과가 많거나 없으면?

  • 컬렉션
    • 결과 없음: 빈 컬렉션 반환
    • List.empty(), Optional.empty()
  • 단건 조회
    - 결과 없음: null 반환
    • 결과가 2건 이상: javax.persistence.NonUniqueResultException 예외 발생
profile
더 좋은 개발자가 되기위한 과정
post-custom-banner

0개의 댓글