기록하는 습관

[JPA] 스프링 데이터 JPA 본문

개발/Java

[JPA] 스프링 데이터 JPA

로그뉴 2023. 12. 7. 14:38

쿼리 메소드 기능

 

1. 메소드 이름으로 쿼리 생성

public interface MemberRepository extends Repository<Member, Long> {
    List<Member> findByEmailAndName(String email, String name);
}

// 실행되는 JPQL
select m from Member m where m.email = ?1 and m.name = ?2

 

** 엔티티의 필드명이 변경되면 인터페이스에 정의한 메소드 이름도 꼭 함께 변경해야 한다. 그렇지 않으면 애플리케이션 시작하는 시점에 오류가 발생한다.

 

2. JPA NamedQuery

// 어노테이션 방식
@Entity
@NamedQuery(
    name="Member.findByUsername",
    query="select m from Member m where m.username = :username")
public class Member {
    ...
}

 

 

스프링 데이터 jpa를 사용하면 메소드 이름만으로 Named 쿼리를 호출할 수 있다.

public interface MemberRepository 
    extends JpaRepository<Member, Long> { // 여기 선언한 Member 도메인 클래스

    List<Member> findByUsername(@Param("username") String username);
}

 

 

스프링 데이터 JPA는 선언한 "도메인 클래스 + .(점) + 메소드 이름"으로 Named 쿼리를 찾아서 실행한다. 위의 예제의 경우, Member.findByUsername이라는 Named 쿼리를 실행한다.

 

3. @Query, 리포지토리 메소드에 쿼리 정의

리포지토리 메소드에 직접 쿼리를 정의하는 방법.

- 실행할 메소드에 정적 쿼리를 직접 작성하므로 이름 없는 Named 쿼리라 할 수 있다.

- JPA Named 애플리케이션 실행 시점에 문법 오류를 발견할 수 있는 것이 장점이다.

public interface MemberRepository extends JpaRepository<Member, Long> {
    @Query("select m from Member m where m.username = ?1")
    Member findByUsername (String username);
}

 

 

Native SQL을 사용하려면 @Query 어노테이션에 nativeQuery = true를 설정한다.

public interface MemberRepository extends JpaRepository<Member, Long> {
    @Query(value = "SELECT * FROM MEMBER WHERE USERNAME = ?0”,
        nativeQuery = true)
    Member findByUsername (String username);
}

 

 

4. 파라미터 바인딩

스프링 데이터 JPA는 위치 기반 파라미터 바인딩과 이름 기반 파라미터 바인딩을 모두 지원한다.

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

 

 

 

5. 벌크성 수정 쿼리

- 스프링 데이터 JPA에서 벌크성 수정, 삭제 쿼리는 Modifying 어노테이션을 사용하면 된다. 

- 벌크성 쿼리를 실행하고 나서 영속성 컨텍스트를 초기화하고 싶으면 @Modifying(clearAutomatically = true)를 사용하면 된다. (기본값은 false)

 

@Modifying
@Query("update Product p set p.price = p.price * 1.1 where
    p.stockAmount < :stockAmount")
int bulkPriceUp(@Param("stockAmount") String stockAmount);

 

 

6. 반환 타입

- 한 건 이상이면 컬렉션 인터페이스 사용, 단건이면 반환 타입을 지정한다.

- 조회 결과가 없으면 컬렉션은 빈 컬렉션을 반환하고, 단건은 null을 반환한다.

- 단건을 기대하고 반환 타입을 지정했는데 결과가 2건 이상 조회되면 NonUniqueResultException 예외가 발생한다.

List<Member> findByName (String name); // 컬렉션
Member findByEmail (String email); // 단건

 

 

7. 페이징과 정렬

- Page를 사용하면 스프링 데이터 JPA는 페이징 기능을 제공하기 위해 검색된 데이터 건수를 조회하는 count 쿼리를 추가로 호출한다.

//count 쿼리 사용
Page<Member> findByName(String name, Pageable pageable);

//count 쿼리 사용 안 함
List<Member> findByName(String name, Pageable pageable);

 

 

//페이징 조건과 정렬 조건 설정
PageRequest pageRequest =
    new PageRequest(0, 10, new Sort(Direction.DESC, "name"));

Page<Member> result =
    memberRepository.findByNameStartingWith("김", pageRequest);

List<Member> members = result.getContent (); //조회된 데이터
int totalPages = result.getTotalPages (); //전체 페이지 수
boolean hasNextPage = result .hasNextPage (); //다음 페이지 존재 여부

 

 

참고) Page 인터페이스

public interface Page<T> extends Iterable<T> {

    int getNumber(); //현재 페이지
    int getSize(); //페이지 크기
    int getTotalPages(); //전체 페이지 수
    int getNumberOfElements(); //현재 페이지에 나올 데이터 수
    long getTotalElements(); //전체 데이터 수
    boolean hasPreviousPage(); //이전 페이지 여부
    boolean isFirstPage(); //현재 페이지가 첫 페이지 인지 여부
    boolean hasNextPage(); //다음 페이지 여부
    boolean isLastPage(); //현재 페이지가 마지막 페이지 인지 여부
    Pageable nextPageable(); //다음 페이지 객체, 다음 페이지가 없으면 null
    Pageable previousPageable(); //다음 페이지 객체, 이전 페이지가 없으면 null
    List<T> getContent(); //조회된 데이터
    boolean hasContent(); //조회된 데이터 존재 여부
    Sort getSort(); //정렬정보

}

 

 

 

명세

- Specification은 컴포지트 패턴으로 구성되어 있어서 여러 Specification을 조합할 수 있다.

 

 

사용자 정의 리포지터리 구현

- 스프링 데이터 JPA로 리포지터리를 개발하면 인터페이스만 정의하고 구현체는 만들지 않는다.

- 메소드를 직접 구현하고 싶다면? -> 리포지터리를 직접 구현하면 공통 인터페이스가 제공하는 기능까지 모두 구현해야 하므로, 스프링 데이터 JPA는 이런 문제를 우회해서 필요한 메소드만 구현할 수 있는 방법을 제공한다.

 

public interface MemberRepositoryCustom {
    public List<Member> findMemberCustom();
}

// 클래스 이름을 짓는 규칙: (repository interface 이름 + Impl)
public class MemberRepositorylmpl implements MemberRepositoryCustom {
    @Override
    public List<Member> findMemberCustom () {
        ... //사용자정의구현
    }
}

 

Comments