Reference. 자바 ORM 표준 JPA 프로그래밍
책 목차 및 이전 글
더보기
들어가기 전 JPA 특징, Q&A
1. JPA 소개
3. 영속성 관리
4. 엔티티 매핑
4.1 - 4.3 @Entity, @Table, 다양한 매핑
4.4 - 4.5 데이터베이스 스키마 자동 생성, DDL 생성 기능
5. 연관관계 매핑 기초
6. 다양한 연관관계 매핑
7. 고급매핑
8. 프록시와 연관관계 관리
9. 값 타입
9.3~5 값 타입과 불변 객체, 값 비교, 값 타입 컬렉션
10. 객체지향 쿼리 언어
11. 웹 애플리케이션 제작
11.1 프로젝트 환경설정
11.2 도메인 모델과 테이블 설계
11.3 애플리케이션 구현
12. 스프링 데이터 JPA
12.4 쿼리 메소드 기능
- 쿼리 메소드 기능은 스프링 데이터 JPA가 제공하는 마법 같은 기능
- 대표적으로 메소드 이름만으로 쿼리를 생성하는 기능
- 스프링 데이터 JPA가 제공하는 쿼리 메소드 기능은 크게 3가지가 존재
- 메소드 이름으로 쿼리 생성
- 메소드 이름으로 JPA NamedQuery 호출
- @Query 어노테이션을 사용해서 리포지토리 인터페이스에 쿼리 직접 정의
12.4.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
- 스프링 데이터 쿼리 생성 관련 주소 : spring-data/jpa/docs Query Creation
스프링 데이터 JPA 쿼리 생성 기능
키워드 예 JPQL 예 Distinct findDistinctByLastnameAndFirstname
select distinct … where x.lastname = ?1 and x.firstname = ?2
And findByLastnameAndFirstname
… where x.lastname = ?1 and x.firstname = ?2
Or findByLastnameOrFirstname
… where x.lastname = ?1 or x.firstname = ?2
Is, Equals findByFirstname
,findByFirstnameIs
,findByFirstnameEquals
… where x.firstname = ?1
Between findByStartDateBetween
… where x.startDate between ?1 and ?2
LessThan findByAgeLessThan
… where x.age < ?1
LessThanEqual findByAgeLessThanEqual
… where x.age <= ?1
GreaterThan findByAgeGreaterThan
… where x.age > ?1
GreaterThanEqual findByAgeGreaterThanEqual
… where x.age >= ?1
After findByStartDateAfter
… where x.startDate > ?1
Before findByStartDateBefore
… where x.startDate < ?1
IsNull, Null findByAge(Is)Null
… where x.age is null
IsNotNull, NotNull findByAge(Is)NotNull
… where x.age not null
Like findByFirstnameLike
… where x.firstname like ?1
NotLike findByFirstnameNotLike
… where x.firstname not like ?1
StartingWith findByFirstnameStartingWith
… where x.firstname like ?1
(parameter bound with appended%
)EndingWith findByFirstnameEndingWith
… where x.firstname like ?1
(parameter bound with prepended%
)Containing findByFirstnameContaining
… where x.firstname like ?1
(parameter bound wrapped in%
)OrderBy findByAgeOrderByLastnameDesc
… where x.age = ?1 order by x.lastname desc
Not findByLastnameNot
… where x.lastname <> ?1
In findByAgeIn(Collection<Age> ages)
… where x.age in ?1
NotIn findByAgeNotIn(Collection<Age> ages)
… where x.age not in ?1
True findByActiveTrue()
… where x.active = true
False findByActiveFalse()
… where x.active = false
IgnoreCase findByFirstnameIgnoreCase
… where UPPER(x.firstname) = UPPER(?1)
- 엔티티의 필드명이 변경되면 인터페이스에 정의한 메소드 이름 변경 필수
- 변경하지 않으면 애플리케이션 시작하는 시점에 오류 발생
12.4.2 JPA NamedQuery
- JPA Named 쿼리는 이름 그대로 쿼리에 이름을 부여해서 사용하는 방법
기존 Named 쿼리 호출 (10.2.15절 참고)
//Entity @Entity @NamedQuery( name="Member.findByUsername", query="select m from Member m where m.username = :username") public class Member { ... } //Repository public class MemberRepository { ... List<Member> resultList = em.createNamedQuery("Member.findByUsername", Member.class) .setParameter("username", "회원1") .getResultList(); }
스프링 데이터 JPA
public interface MemberRepository extends JpaRepository<Member, Long> { List<Member> findByUsername(@Param("username") String username); }
- 스프링 데이터 JPA는 "도메인 클래스 + .(점) + 메소드 이름" 으로 Named쿼리를 찾아 실행
- 만약 Named쿼리가 없으면 메소드 이름으로 쿼리 생성 전략 사용
- Param은 이름 기반 파라미터를 바인딩할 때 사용하는 어노테이션
12.4.3 @Query, 리포지토리 메소드에 쿼리 정의
- 리포지토리 메소드에 직접 쿼리를 정의하려면 @Query 어노테이션을 사용
- JPA Named 쿼리처럼 애플리케이션 실행 시점에 문법 오류 발견이 가능
public interface MemberRepository extends JpaRepository<Member, Long> { @Query("select m from Member m where m.username = ?1") Member findByUsername(String username); }
- 네이티브 SQL을 사용하려면 @Query 어노테이션에 nativeQuery = true를 설정
- 스프링 데이터 JPA의 파라미터 바인딩의 경우 JPQL은 위치 기반 파라미터를 1부터 시작 네이티브 SQL은 0부터 시작
public interface MemberRepository extends JpaRepository<Member, Long> { @Query(value = "SELECT * FROM MEMBER WHERE USERNAME = ?0", nativeQuery = true) Member findByUsername(String username); }
12.4.4 파라미터 바인딩
- 스프링 데이터 JPA는 위치 기반 파라미터 바인딩과 이름 기반 파라미터 바인딩을 모두 지원
select m from Member m where m.username = ?1 //위치 기반 select m from Member m where m.username = :name //이름 기반
- 기본값은 위치 기반이며 파라미터 순서로 바인딩
- 이름 기반 파라미터 바인딩을 사용하려면 @Param 어노테이션을 사용
import org.springframework.data.repository.query.Param public interface MemberRepository extends JpaRepository<Member, Long> { @Query("select m from Member m where m.username = :name") Member findByUsername(@Param("name") String username); }
- 코드 가독성과 유지보수를 위해 이름 기반 파라미터 바인딩을 사용
12.4.5 벌크성 수정 쿼리
- JPA로 작성한 벌크성 수정 쿼리
int bulkPriceUp(String stockAmount) { ... String qlString = "update Product p set p.price = p.price * 1.1 where p.stockAmount < :stockAmount"; int resultCount = em.createQuery(qlString) .setParameter("stockAmount", stockAmount) .executeUpdate(); }
- 스프링 데이터 JPA를 사용한 벌크성 수정 쿼리
@Modifying @Query("update Product p set p.price = p.price * 1.1 where p.stockAmount < :stockAmount") int bulkPriceUp(@Param("stockAmount") String stockAmount);
- 스프링 데이터 JPA에서 벌크성 수정, 삭제 쿼리는 Modifying 어노테이션을 사용
- 쿼리 후 영속성 컨텍스트를 초기화하고 싶으면
@Modifying(clearAutomatically = true)
처럼 clearAutomatically 옵션을 true로 설정 (기본 값은 false)
12.4.6 반환 타입
- 스프링 데이터 JPA는 유연한 반환 타입을 지원
결과가 한 건 이상이면 컬렉션 인터페이스, 단건이면 반환 타입을 지정
List<Member> findByName(String name); //컬렉션 Member findByEmail(String email); //단건
- 조회 결과가 없으면 컬렉션은 빈 컬렉션, 단건은 null을 반환
- 단건의 경우 2건 이상 조회되면
NonUniqueResultException
예외가 발생- 단건으로 지정한 메소드를 호출하면 JPQL의
Query.getSingleResult()
메소드를 호출
- 단건으로 지정한 메소드를 호출하면 JPQL의
12.4.7 페이징과 정렬
- 스프링 데이터 JPA는 쿼리 메소드에 페이징과 정렬을 사용 가능한 2가지 특별 파라미터 제공
org.springframework.data.domain.Sort
: 정렬 기능
org.springframework.data.domain.Pagable
: 페이징 기능(내부에 Sort 포함)
- 파라미터에 Pageable을 사용하면 반환 타입으로 List나 Page를 사용 가능
- 반환 타입이 Page면 페이징 기능을 제공하기 위해 전체 데이터 건수 조회 쿼리를 추가 호출
//count 쿼리 사용 (전체 데이터 건수 조회 쿼리) Page<Member> findByName(String name, Pageable pageable); //count 쿼리 사용 안 함 List<Member> findByName(String name, Pageable pageable); List<Member> findByName(String name, Sort sort);
- 아래의 조건으로 페이징과 정렬을 사용하는 예제
- 검색 조건: 이름이 김으로 시작하는 회원
- 정렬 조건: 이름으로 내림차순
- 페이징 조건: 첫 번쨰 페이지, 페이지당 보여줄 데이터는 10건
public interface MemberRepository extends Repository<Member, Long> { Page<Member> findByNameStartingWith(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(); //다음 페이지 존재 여부
- 두 번째 파라미터
Pageabled
은 인터페이스, 따라서 구현체인PageRequest
객체 사용
PageRequest
생성자의 첫 번쨰 파라미터는 현재 페이지, 두 번째는 조회할 데이터 수를 입력 추가로 정렬 정보도 파라미터로 사용 가능
- 반환 타입인 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(); //현재 페이지가 마지막 페이지 인지 여부 Pagable nextPageable(); //다음 페이지 객체, 다음 페이지가 없으면 null Pagable previousPageable(); //다음 페이지 객체, 이전 페이지가 없으면 null List<T> getContent(); //조회된 데이터 boolean hasContent(); //조회된 데이터 존재 여부 Sort getSort(); //정렬 정보 }
12.4.8 힌트
- JPA 힌트를 사용하려면 @QueryHints 어노테이션을 사용
- 참고로 이것은 SQL 힌트가 아니라 JPA 구현체에게 제공하는 힌트
@QueryHints(value = { @QueryHint(name = "org.hibernate.readOnly" , value = "true") }, forCounting = true) Page<Member> findByName(String name, Pagable Pageable);
- forCounting 속성은 반환 타입으로 Page 인터페이스를 적용하면 추가로 count 쿼리에도 쿼리 힌트를 적용할지를 설정하는 옵션 (기본값 true)
12.4.9 Lock
- 쿼리 시 락을 걸려면 @Lock 어노테이션을 사용 (16.1절 참고)
@Lock(LockModeType.PESSIMISTIC_WRITE) List<Member> findByName(String name);
'개발서적 > 자바 ORM 표준 JPA' 카테고리의 다른 글
[자바 ORM 표준 JPA 프로그래밍] 13.1 트랜잭션 범위의 영속성 컨텍스트 (0) | 2021.09.21 |
---|---|
[자바 ORM 표준 JPA 프로그래밍] 12.5~10 명세, 사용자 정의 리포지토리, Web 확장... (0) | 2021.09.21 |
[자바 ORM 표준 JPA 프로그래밍] 12.1~3 스프링 데이터 JPA 소개, 공통 인터페이스 기능 (0) | 2021.09.21 |
[자바 ORM 표준 JPA 프로그래밍] 10.6 객체지향 쿼리 심화 (0) | 2021.09.13 |
[자바 ORM 표준 JPA 프로그래밍] 10.5 네이티브 SQL (0) | 2021.09.13 |