백엔드/Spring

[Spring] 쿼리 메서드

kwang2134 2024. 9. 23. 14:40
728x90
반응형
728x90

쿼리 메서드

  • 데이터베이스 쿼리를 생성하기 위해 메서드 이름을 사용하여 간편하게 구현할 수 있는 기능
  • 개발자가 쿼리를 작성할 때 SQL을 직접 작성하는 대신 메서드 이름을 기반으로 JPA가 자동으로 쿼리를 생성
  • JpaRepository를 상속받는 인터페이스에 구현

기본 구조

  • 접두어를 사용한 메서드 이름으로 조건을 선택
  • 조회: find…By , read…By , query…By get…By,... 에 주로 식별하기 위한 이름이나 설명이 들어가거나 생략 가능
//예시: 둘 다 username을 통해 조회하는 메서드
List<User> findByUsername(String username)
List<User> findUserByUsername(String username)
  • COUNT: count…By,  count를 반환하는 메서드 -> 반환 타입 long
//예시: 특정 나이의 User 객체 수 반환
long countByAge(int age)
long countUserByAge(int age)
  • EXISTS: exists…By, 존재 여부를 반환하는 메서드 -> 반환타입 boolean
//예시: 특정 이름의 User가 존재하는지 확인
boolean existsByUsername(String name)
boolean existsUserByUsername(String name)
  • 삭제: delete…By, remove…By, 데이터를 삭제하는 메서드 -> 반환타입 long, int, void 
//예시: 특정 이름의 User를 삭제하고 처리한 결과 개수를 반환
long deleteByUsername(String username)
long deleteUserByUsername(String username)
  • DISTINCT: findDistinct, findMemberDistinctBy, 중복된 데이터를 제거하고 고유한 결과만 반환 find에 Distinct 키워드만 추가 
//예시: 특정 이름의 User 객체 중 중복을 제거하고 반환
List<User> findDistinctByUsername(String username)
  • LIMIT: findFirst3, findFirst, findTop, findTop3, 반환할 결과의 개수를 제한
//예시: 나이가 가장 많은 User 객체 반환 
User findFirstByOrderByAgeDesc()

 

기본 JPA NamedQuery

  • 사전 정의된 쿼리로 코드에서 재사용할 수 있도록 이름을 붙인 쿼리
  • 실행 시 컴파일되어 사전에 쿼리 오류를 확인 가능

사용 방법

  • 엔티티 클래스에 @NamedQuery 어노테이션으로 정의
//예시
@NamedQuery(name="User.findByUsername", query="select u from User u where u.username = :username")
  • 사용 시 엔티티 매니저의 createNamedQuery로 호출하여 사용
//예시
createNamedQuery(" User.findByUsername", User.class)

 

Spring Data Jpa NamedQuery

  • 구현한 Repository에서 선언한 메서드 위 @Query 어노테이션의 name 속성으로 사용
  • @Query가 없더라도 JPA는 도메인 클래스 이름 + . + 메서드 이름으로 Named 쿼리를 찾아서 실행
//예시 
@Query(name = " User.findByUsername") 
List<User> findByUsername (@Param("username") String username);

 

Repository 쿼리 메서드

  • 리포지토리에 작성한 메서드에 직접 쿼리를 정의하여 사용하는 방법
  • @Query 어노테이션을 통해 사용
  • 이름 기반의 쿼리 메서드는 조건이 많아질수록 이름이 길어져 @Query를 통해 정의한 방법을 주로 사용 
//예시
@Query("select u from User u where u.username= :username and u.age = :age") 
List<User> findUser(@Param("username") String username, @Param("age") int age);

 

@Query DTO 조회

  • JPA와 같이 new 명령어를 통해 DTO 를 사용한 조회 가능 
@Query("select new com.example.dto.UserDto(u.id, u.username, g.name) " +
 "from User u join u.group g")
List<UserDto> findUserDto();

 

파라미터 바인딩

  • 위치 기반과 이름 기반 파라미터 바인딩 제공
  • 컬렉션 타입 파라미터로 IN 절 지원 
select u from User u where u.username = ?0 //위치 기반
select u from User u where u.username = :name //이름 기반

//컬렉션 타입
@Query("select u from User u where u.username in :names")
List<User> findByNames(@Param("names") List<String> names)

 

반환 타입

  • 유연한 반환 타입 제공, (단건, 컬렉션, Optional)
  • 조회 결과에 따른 반환
    • 컬렉션
      • 결과 없음 -> 빈 컬렉션 반환
    • 단건
      • 결과 없음 -> null
      • 2개 이상 -> javax.persistence.NonUniqueResultException 예외 발생
//예시
List<User> findByUsername(String name); //컬렉션
User findByUsername(String name); //단건
Optional<User> findByUsername(String name); //단건 Optional

 

Paging

  • 페이징 기능 사용시 Page, Slice 반환 타입 제공
  • Page <T>: 추가 count 쿼리 결과를 포함하는 페이징
  • Slice <T>: 추가 count 쿼리 없이 다음 페이지만 확인 가능(요청한 페이지 크기보다 하나 더 많은 항목을 조회) 
  • Pageable 인터페이스로 페이징 정보 정의
  • 추가적으로 정렬 조건 정의 가능 
  • 페이지 시작은 0 
  • map 메서드 제공으로 페이징을 유지한 채 DTO 변환 가능 
//예시
Page<User> findByUsername(String name, Pageable pageable);  //count 쿼리 사용
Slice<User> findByUsername(String name, Pageable pageable); //count 쿼리 사용 안함
List<User> findByUsername(String name, Pageable pageable);  //count 쿼리 사용 안함

//페이징 정보를 담은 구현체를 파라미터로 사용
PageRequest pageRequest = PageRequest.of(0, 3, Sort.by(Sort.Direction.DESC, "username"));

//map을 사용한 DTO 변환
Page<UserDto> dtoPage = page.map(u -> new UserDto());

 

주요 메서드

  • getTotalPages(): 전체 페이지 수 반환
  • getTotalElements(): 전체 데이터 수 반환
  • getNumber(): 현재 페이지 반환
  • getSize(): 페이지 크기 반환
  • getNumberOfElements(): 현재 페이지에 나올 데이터 수 반환
  • getContent(): 조회된 데이터 반환
  • hasContent(): 조회된 데이터 존재 여부 반환
  • isFirst(), isLast(): 현재 페이지가 첫(마지막) 페이지 인지 반환
  • hasNext(), hasPrevious(): 다음(이전) 페이지 존재 여부 반환

벌크 쿼리

  • 여러 레코드를 동시에 업데이트할 때 사용하는 쿼리
  • 벌크 수정 삭제 쿼리는 @Modifying 어노테이션을 사용
  • clearAutomatically 옵션으로 벌크 쿼리 실행 후 영속성 컨텍스트 초기화 가능 (기본값 false)
  • 영향을 받은 엔티티 수를 반환 
//예시
@Modifying
@Query("UPDATE User u SET u.age = u.age + 1")
int incrementAllUserAges();

 

@EntityGraph

  • 특정 엔티티를 조회할 때 관련된 엔티티를 미리 로딩
  • 지연 로딩 설정 된 객체를 fetch join을 사용하는 것과 동일한 기능
  • @EntityGraph 어노테이션 통해 적용
  • attributepaths 속성으로 즉시 로딩 할 객체를 선택 
//공통 메서드 오버라이드
@Override
@EntityGraph(attributePaths = {"group"})
List<User> findAll();

//JPQL + 엔티티 그래프
@EntityGraph(attributePaths = {"group"})
@Query("select u from User u")
List<User> findMemberEntityGraph();

//쿼리 메서드 
@EntityGraph(attributePaths = {"group"})
List<User> findByUsername(String username)

실전! 스프링 데이터 JPA 강의 | 김영한 - 인프런 (inflearn.com) 강의 내용 참고

 

 

728x90

'백엔드 > Spring' 카테고리의 다른 글

[Spring] Querydsl  (1) 2024.09.25
[Spring] Spring Data Jpa 확장 기능  (0) 2024.09.24
[Spring] Spring Data JPA  (2) 2024.09.22
[Spring] JPA 컬렉션 조회 최적화 & OSIV  (1) 2024.09.21
[Spring] JPA 지연 로딩 & 조회 성능 최적화  (0) 2024.09.20