일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- 백준 20055 컨베이어 벨트 위의 로봇
- 싱글톤 컨테이너
- Kotlin in action 10장
- 백준
- 코틀린
- Kotlin in action 6장
- 13460 구슬탈출 2
- 컨베이어 벨트 위의 로봇 Python
- Kotlin
- Python
- 스프링 핵심 원리 - 기본편
- 코틸린인액션
- KotlinInAction
- 기능개발 python
- 20055 컨베이어 벨트 위의 로봇
- spring
- 7장 고급매핑
- Kotlin in action 5장
- 백준 13460 Python
- 코틀린인액션
- 스프링 컨테이너와 스프링 빈
- 스프링 핵심 원리 이해
- 스프링 핵심 원리
- 객체 지향 설계와 스프링
- 고급매핑
- 20055
- Kotlin in action 3장
- kotlin in action 정리
- Kotlin In Action
- 자바 ORM 표준 JPA 프로그래밍 7장
- Today
- Total
기록하는 습관
[JPA] 스프링 데이터 JPA 본문
쿼리 메소드 기능
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 () {
... //사용자정의구현
}
}