스프링 데이터 JPA #Reference:쿼리
5.3. 쿼리 방법
이 섹션에서는 Spring Data JPA로 쿼리를 작성하는 다양한 방법에 대해 설명합니다.
5.3.1. 쿼리 조회 전략
JPA 모듈은 쿼리를 수동으로 문자열로 정의하거나 메소드 이름에서 파생되도록 지원합니다.
술어와 파생 쿼리는 IsStartingWith
, StartingWith
, StartsWith
, IsEndingWith
, EndingWith
, EndsWith
, IsNotContaining
, NotContaining
, NotContains
, IsContaining
, Containing
, Contains
이러한 쿼리에 대한 각각의 인수는 살균 얻을 것이다. 즉, 인수에 실제로 LIKE
와일드 카드로 인식되는 문자가 포함되어 있으면 이스케이프되어 리터럴로만 일치합니다. 사용되는 이스케이프 문자는 설정을 구성 할 수 escapeCharacter
의 @EnableJpaRepositories
주석. SpEL 표현식 사용 과 비교하십시오 .
선언 된 쿼리
메소드 이름에서 파생 된 쿼리를 얻는 것이 매우 편리하지만 메소드 이름 구문 분석기가 사용하려는 키워드를 지원하지 않거나 메소드 이름이 불필요하게 추악 해지는 상황에 직면 할 수 있습니다. 따라서 명명 규칙을 통해 JPA 명명 된 쿼리 를 사용 하거나 (자세한 내용은 JPA 명명 된 쿼리 사용 참조 ) 쿼리 방법에 주석을 달 수 있습니다 @Query
(자세한 내용은 사용@Query
참조).
5.3.2. 쿼리 생성
일반적으로 JPA에 대한 쿼리 생성 메커니즘은“ 쿼리 방법 ”에 설명 된대로 작동합니다 . 다음 예제는 JPA 쿼리 메소드가 변환하는 내용을 보여줍니다.
공용 인터페이스 UserRepository는 Repository <User, Long>을 확장합니다. {
List <User> findByEmailAddressAndLastname (String emailAddress, String lastname);
}
JPA 기준 API를 사용하여 쿼리를 작성하지만 기본적으로 다음 쿼리로 변환됩니다 select u from User u where u.emailAddress = ?1 and u.lastname = ?2
. Spring Data JPA는 " 속성 표현식 "에 설명 된대로 속성 확인을 수행하고 중첩 된 속성을 통과 합니다.
다음 표는 JPA에서 지원되는 키워드 및 해당 키워드를 포함하는 메소드가 변환하는 내용을 설명합니다.
예어 | 견본 | JPQL 스 니펫 |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
In 그리고 NotIn 또한 어떤 서브 클래스 걸릴 Collection 뿐만 aparameter뿐만 아니라 배열이나 변수 인수를. 동일한 논리 연산자의 다른 구문 버전은“ 리포지토리 쿼리 키워드 ”를 확인하십시오 . |
5.3.3. JPA 명명 된 쿼리 사용
예제는 <named-query /> 요소와 @NamedQuery 주석을 사용합니다 . 이러한 구성 요소에 대한 쿼리는 JPA 쿼리 언어로 정의해야합니다. 물론 사용할 <named-native-query /> 수도 @NamedNativeQuery 있습니다. 이러한 요소를 사용하면 데이터베이스 플랫폼 독립성을 잃어 네이티브 SQL에서 쿼리를 정의 할 수 있습니다. |
XML 명명 된 쿼리 정의
XML 구성을 사용하려면 필요한 <named-query />
요소를 클래스 경로 폴더에 있는 orm.xml
JPA 구성 파일에 추가하십시오 META-INF
. 명명 된 쿼리의 자동 호출은 정의 된 명명 규칙을 사용하여 활성화됩니다. 자세한 내용은 아래를 참조하십시오.
<named-query name="User.findByLastname">
<query>select u from User u where u.lastname = ?1</query>
</named-query>
쿼리에는 런타임시이를 해결하는 데 사용되는 특수 이름이 있습니다.
주석 기반 구성
주석 기반 구성은 다른 구성 파일을 편집 할 필요가 없어 유지 관리 노력이 줄어 듭니다. 모든 새 쿼리 선언에 대해 도메인 클래스를 다시 컴파일해야하므로 그 이점을 지불해야합니다.
@Entity
@NamedQuery(name = "User.findByEmailAddress",
query = "select u from User u where u.emailAddress = ?1")
public class User {
}
인터페이스 선언
이러한 명명 된 쿼리를 실행하려면 UserRepository
다음을 지정하십시오 .
public interface UserRepository extends JpaRepository<User, Long> {
List<User> findByLastname(String lastname);
User findByEmailAddress(String emailAddress);
}
Spring Data는 구성된 도메인 클래스의 간단한 이름부터 시작하여 점으로 구분 된 메소드 이름으로 명명 된 쿼리에 대한 이러한 메소드 호출을 해결하려고 시도합니다. 따라서 앞의 예제는 메소드 이름에서 조회를 작성하는 대신 examlpe에 정의 된 이름 지정된 조회를 사용합니다.
5.3.4. 사용@Query
명명 된 쿼리를 사용하여 엔터티에 대한 쿼리를 선언하는 것은 유효한 방법이며 적은 수의 쿼리에 적합합니다. 쿼리 자체는이를 실행하는 Java 메소드에 연결되어 있으므로 실제로 스프링 데이터 JPA @Query
어노테이션을 사용하여 도메인 클래스에 주석을 달지 않고 직접 바인딩 할 수 있습니다 . 이렇게하면 도메인 클래스가 지속성 특정 정보에서 벗어나 쿼리를 리포지토리 인터페이스에 배치 할 수 있습니다.
쿼리 메서드에 주석이 달린 @NamedQuery
쿼리는에 선언 된 또는 명명 된 쿼리를 사용하여 정의 된 쿼리보다 우선합니다 orm.xml
.
다음 예제는 @Query
주석으로 작성된 쿼리를 보여줍니다 .
@Query
public interface UserRepository extends JpaRepository<User, Long> {
@Query("select u from User u where u.emailAddress = ?1")
User findByEmailAddress(String emailAddress);
}
고급 LIKE
표현식 사용
다음 예제와 같이 수동으로 정의 된 쿼리를 @Query
사용하여 쿼리 실행 메커니즘을 사용 LIKE
하면 쿼리 정의 내에서 고급 식을 정의 할 수 있습니다 .
like
@Query의 고급 표현식public interface UserRepository extends JpaRepository<User, Long> {
@Query("select u from User u where u.firstname like %?1")
List<User> findByFirstnameEndsWith(String firstname);
}
위의 예에서 LIKE
구분 기호 문자 ( %
)가 인식되고 쿼리가 유효한 JPQL 쿼리로 변환됩니다 ( %
). 쿼리 실행시 메소드 호출에 전달 된 매개 변수는 이전에 인식 된 LIKE
패턴으로 기능 보강됩니다 .
네이티브 쿼리
@Query
주석은 설정하여 기본 쿼리를 실행을 허용 nativeQuery
다음 예에서와 같이, 참으로 플래그 :
public interface UserRepository extends JpaRepository<User, Long> {
@Query(value = "SELECT * FROM USERS WHERE EMAIL_ADDRESS = ?1", nativeQuery = true)
User findByEmailAddress(String emailAddress);
}
Spring Data JPA는 현재 네이티브 쿼리에 대해 동적 정렬을 지원하지 않습니다. 네이티브 쿼리에 대해 신뢰할 수없는 선언 된 실제 쿼리를 조작해야하기 때문입니다. 그러나 다음 예제와 같이 카운트 쿼리를 직접 지정하여 페이지 매김에 네이티브 쿼리를 사용할 수 있습니다. |
@Query
public interface UserRepository extends JpaRepository<User, Long> {
@Query(value = "SELECT * FROM USERS WHERE LASTNAME = ?1",
countQuery = "SELECT count(*) FROM USERS WHERE LASTNAME = ?1",
nativeQuery = true)
Page<User> findByLastname(String lastname, Pageable pageable);
}
유사한 접근 방식은 쿼리 .count
사본에 접미사를 추가하여 명명 된 기본 쿼리에도 작동 합니다. 그러나 카운트 쿼리에 대한 결과 집합 매핑을 등록해야 할 수도 있습니다.
5.3.5. 정렬 사용
정렬은을 제공 PageRequest
하거나 Sort
직접 사용하여 수행 할 수 있습니다 . Order
인스턴스 내에서 실제로 사용되는 속성은 Sort
도메인 모델과 일치해야하므로 쿼리에 사용 된 속성 또는 별칭으로 확인해야합니다. JPQL은 이것을 상태 필드 경로 표현식으로 정의합니다.
참조 불가능한 경로 표현식을 사용하면 Exception . |
그러나 Sort
함께 @Query
사용하면 절 Order
내에 함수가 포함 된 경로 검사되지 않은 인스턴스 를 몰래 사용할 수 있습니다 ORDER BY
. Order
주어진 쿼리 문자열에가 추가 되었기 때문에 가능 합니다. 기본적으로 Spring Data JPA Order
는 함수 호출을 포함하는 모든 인스턴스를 거부 하지만 JpaSort.unsafe
잠재적으로 안전하지 않은 순서를 추가 하는 데 사용할 수 있습니다.
다음 예제 사용 Sort
과 JpaSort
에 안전하지 않은 옵션을 포함 JpaSort
:
Sort
및JpaSort
public interface UserRepository extends JpaRepository<User, Long> {
@Query("select u from User u where u.lastname like ?1%")
List<User> findByAndSort(String lastname, Sort sort);
@Query("select u.id, LENGTH(u.firstname) as fn_len from User u where u.lastname like ?1%")
List<Object[]> findByAsArrayAndSort(String lastname, Sort sort);
}
repo.findByAndSort("lannister", new Sort("firstname")); (1)
repo.findByAndSort("stark", new Sort("LENGTH(firstname)")); (2)
repo.findByAndSort("targaryen", JpaSort.unsafe("LENGTH(firstname)")); (3)
repo.findByAsArrayAndSort("bolton", new Sort("fn_len")); (4)
1 | Sort 도메인 모델의 속성을 가리키는 유효한 식입니다. |
2 | 잘못된 Sort 함수 호출이 포함되어 있습니다. Thows 예외. |
3 | Sort 명시 적으로 안전하지 않은 유효합니다 Order . |
4 | Sort 별명 함수를 가리키는 유효한 표현식. |
5.3.6. 명명 된 매개 변수 사용
기본적으로 Spring Data JPA는 앞의 모든 예제에서 설명한대로 위치 기반 매개 변수 바인딩을 사용합니다. 따라서 매개 변수 위치와 관련하여 리팩토링 할 때 쿼리 메소드가 약간 오류가 발생하기 쉽습니다. 이 문제를 해결하기 위해 @Param
다음 예제와 같이 주석을 사용하여 메서드 매개 변수에 구체적인 이름을 지정하고 쿼리에서 이름을 바인딩 할 수 있습니다 .
public interface UserRepository extends JpaRepository<User, Long> {
@Query("select u from User u where u.firstname = :firstname or u.lastname = :lastname")
User findByLastnameOrFirstname(@Param("lastname") String lastname,
@Param("firstname") String firstname);
}
메소드 매개 변수는 정의 된 쿼리에서 순서에 따라 전환됩니다. |
버전 4부터 Spring은 -parameters 컴파일러 플래그를 기반으로 Java 8의 매개 변수 이름 검색을 완벽하게 지원합니다 . 정보를 디버그하는 대안으로 빌드에서이 플래그를 사용하면 @Param 명명 된 매개 변수에 대한 주석을 생략 할 수 있습니다 . |
5.3.7. SpEL 표현식 사용
Spring Data JPA 릴리스 1.4부터로 정의 된 수동 정의 쿼리에서 제한된 SpEL 템플릿 표현식의 사용을 지원합니다 @Query
. 쿼리 실행시 이러한 표현식은 사전 정의 된 변수 세트에 대해 평가됩니다. Spring Data JPA는라는 변수를 지원합니다 entityName
. 사용법은 select x from #{#entityName} x
입니다. entityName
주어진 저장소와 연관된 도메인 유형을 삽입 합니다. 은 entityName
도메인 유형이의 이름과 속성을 설정 한 경우 다음과 같이 해결 될 때 @Entity
주석이 사용된다. 그렇지 않으면 도메인 유형의 단순 클래스 이름이 사용됩니다.
다음 예제 #{#entityName}
는 쿼리 메소드와 수동으로 정의 된 쿼리로 저장소 인터페이스를 정의하려는 쿼리 문자열 의 표현식에 대한 유스 케이스를 보여줍니다 .
@Entity
public class User {
@Id
@GeneratedValue
Long id;
String lastname;
}
public interface UserRepository extends JpaRepository<User,Long> {
@Query("select u from #{#entityName} u where u.lastname = ?1")
List<User> findByLastname(String lastname);
}
@Query
주석 의 쿼리 문자열에 실제 엔터티 이름이 표시되지 않도록 #{#entityName}
변수를 사용할 수 있습니다 .
는 entityName 사용하여 사용자 정의 할 수 있습니다 @Entity 주석을. orm.xml SpEL 표현식에 대한 사용자 정의는 지원되지 않습니다. |
물론 User
쿼리 선언에서 직접 사용할 수도 있지만 쿼리도 변경해야합니다. 참조 #entityName
는 User
클래스 의 향후 재 매핑을 다른 엔티티 이름 으로 가져옵니다 (예 :을 사용하여) @Entity(name = "MyUser")
.
#{#entityName}
쿼리 문자열 의 표현식에 대한 또 다른 사용 사례 는 구체적 도메인 유형에 대한 특수 저장소 인터페이스를 사용하여 일반 저장소 인터페이스를 정의하려는 경우입니다. 구체적 인터페이스에서 사용자 정의 조회 메소드의 정의를 반복하지 않으려면 @Query
다음 예제와 같이 일반 저장소 인터페이스에서 주석 의 조회 문자열에 엔티티 이름 표현식을 사용할 수 있습니다 .
@MappedSuperclass
public abstract class AbstractMappedType {
…
String attribute
}
@Entity
public class ConcreteType extends AbstractMappedType { … }
@NoRepositoryBean
public interface MappedTypeRepository<T extends AbstractMappedType>
extends Repository<T, Long> {
@Query("select t from #{#entityName} t where t.attribute = ?1")
List<T> findAllByAttribute(String attribute);
}
public interface ConcreteRepository
extends MappedTypeRepository<ConcreteType> { … }
앞의 예에서 MappedTypeRepository
인터페이스는 몇 개의 도메인 유형이 확장되는 공통 상위 인터페이스입니다 AbstractMappedType
. 또한 findAllByAttribute(…)
특수 저장소 인터페이스의 인스턴스에서 사용할 수있는 일반 메소드 도 정의합니다 . 이제 findByAllAttribute(…)
on을 호출 ConcreteRepository
하면 쿼리가됩니다 select t from ConcreteType t where t.attribute = ?1
.
인수를 조작하기위한 SpEL 표현식을 사용하여 메소드 인수를 조작 할 수도 있습니다. 이러한 SpEL 표현식에서는 엔티티 이름을 사용할 수 없지만 인수는 사용할 수 있습니다. 다음 예에서 설명하는 것처럼 이름이나 색인으로 액세스 할 수 있습니다.
@Query("select u from User u where u.firstname = ?1 and u.firstname=?#{[0]} and u.emailAddress = ?#{principal.emailAddress}")
List<User> findByFirstnameAndCurrentUserWithCustomQuery(String firstname);
-조건의 경우 like
종종 %
문자열 값 매개 변수의 시작 또는 끝에 적용 하려고합니다 . 바인드 매개 변수 표시 문자 또는 SpEL 표현식을로 추가하거나 접 두부를 붙여서 수행 할 수 있습니다 %
. 다시 다음 예제는이를 보여줍니다.
@Query("select u from User u where u.lastname like %:#{[0]}% and u.lastname like %:lastname%")
List<User> findByLastnameWithSpelExpression(@Param("lastname") String lastname);
like
안전하지 않은 소스에서 나오는 값에 -conditions를 사용 하는 경우 값을 삭제하여 와일드 카드를 포함 할 수 없으므로 공격자가 가능한 것보다 많은 데이터를 선택할 수 있습니다. 이를 위해이 escape(String)
방법을 SpEL 컨텍스트에서 사용할 수 있습니다. 그것은 모든 인스턴스 접두사 _
와 %
두 번째 인수에서 하나의 문자로 첫번째 인수를. JPQL 및 표준 SQL에서 사용 가능한 표현식 escape
절 과 함께 like
사용하면 바인드 매개 변수를 쉽게 정리할 수 있습니다.
@Query("select u from User u where u.firstname like %?#{escape([0])}% escape ?#{escapeCharacter()}")
List<User> findContainingEscaped(String namePart);
저장소 인터페이스에서이 메소드를 선언 findContainingEscaped("Peter_")" will find `Peter_Parker
했지만 지정하지 않았습니다 Peter Parker
. 사용되는 이스케이프 문자는 설정을 구성 할 수 escapeCharacter
의 @EnableJpaRepositories
주석. escape(String)
SpEL 컨텍스트에서 사용 가능한 메소드 는 SQL 및 JPQL 표준 와일드 카드 _
및 이스케이프 만 이스케이프합니다 %
. 기본 데이터베이스 또는 JPA 구현이 추가 와일드 카드를 지원하면 이스케이프되지 않습니다.
5.3.8. 쿼리 수정
이전의 모든 섹션은 주어진 엔티티 또는 엔티티 콜렉션에 액세스하기 위해 쿼리를 선언하는 방법을 설명합니다. “ Spring Data Repositories에 대한 사용자 정의 구현 ”에 설명 된 기능을 사용하여 사용자 정의 수정 동작을 추가 할 수 있습니다 . 이 접근 방식은 포괄적 인 사용자 지정 기능에 적합 @Modifying
하므로 다음 예제와 같이 쿼리 방법에 주석을 달아 매개 변수 바인딩 만 필요한 쿼리를 수정할 수 있습니다 .
@Modifying
@Query("update User u set u.firstname = ?1 where u.lastname = ?2")
int setFixedFirstnameFor(String firstname, String lastname);
이렇게하면 선택 쿼리 대신 메서드에 주석이 달린 쿼리가 업데이트 쿼리로 트리거됩니다. (가)로 EntityManager
개질 쿼리의 실행 후 오래된 엔티티를 포함 할 수 있습니다, 우리는 자동으로합니다 (참조 지우지 마십시오 의 JavaDoc 의 EntityManager.clear()
이 효과적으로 여전히 계류중인 모든 플러시되지 않은 변경을 줄어들 기 때문에 자세한 내용을) EntityManager
. 을 EntityManager
자동으로 지우려면 @Modifying
주석의 clearAutomatically
속성을으로 설정할 수 있습니다 true
.
@Modifying
주석은와 함께에만 관련 @Query
주석. 파생 쿼리 메서드 또는 사용자 지정 메서드에는이 주석이 필요하지 않습니다.
파생 된 삭제 쿼리
Spring Data JPA는 또한 다음 예제와 같이 JPQL 쿼리를 명시 적으로 선언하지 않아도되는 파생 삭제 쿼리를 지원합니다.
interface UserRepository extends Repository<User, Long> {
void deleteByRoleId(long roleId);
@Modifying
@Query("delete from User u where u.role.id = ?1")
void deleteInBulkByRoleId(long roleId);
}
있지만 deleteByRoleId(…)
그것은 기본적으로 같은 결과를 같은 방법 보이는 deleteInBulkByRoleId(…)
, 그것들이 실행 얻을 방법의 측면에서 두 개의 메소드 선언 사이에는 중요한 차이가있다. 이름에서 알 수 있듯이 후자의 방법은 데이터베이스에 대해 단일 JPQL 쿼리 (주석에 정의 된 쿼리)를 실행합니다. 이것은 현재로드 된 인스턴스조차도 User
수명주기 콜백이 호출되지 않음을 의미 합니다.
수명주기 쿼리가 실제로 호출되도록하기 위해 호출은 deleteByRoleId(…)
쿼리 를 실행 한 다음 리턴 된 인스턴스를 하나씩 삭제하여 지속성 제공자가 실제로 @PreRemove
해당 엔티티에서 콜백을 호출 할 수 있도록합니다 .
실제로 파생 된 삭제 쿼리는 쿼리를 실행 한 다음 CrudRepository.delete(Iterable<User> users)
결과 를 호출 하고의 다른 delete(…)
메서드 구현과 동기화 된 동작을 유지 하기위한 바로 가기입니다 CrudRepository
.
5.3.9. 쿼리 힌트 적용
리포지토리 인터페이스에 선언 된 쿼리에 JPA 쿼리 힌트를 적용하려면 @QueryHints
주석을 사용할 수 있습니다 . @QueryHint
다음 예제와 같이 페이지 매김을 적용 할 때 트리거 된 추가 카운트 쿼리에 적용된 힌트를 잠재적으로 비활성화하려면 JPA 주석 배열과 부울 플래그가 필요합니다.
public interface UserRepository extends Repository<User, Long> {
@QueryHints(value = { @QueryHint(name = "name", value = "value")},
forCounting = false)
Page<User> findByLastname(String lastname, Pageable pageable);
}
앞의 선언은 @QueryHint
실제로 해당 쿼리에 대해 구성된 구성 을 적용하지만 총 페이지 수를 계산하기 위해 트리거 된 카운트 쿼리에는 적용하지 않습니다.
5.3.10. 페치 및로드 그래프 구성
JPA 2.1 사양에는 @EntityGraph
주석 과 함께 지원되는 Fetch- 및 LoadGraphs 지정에 대한 지원이 도입 되어 @NamedEntityGraph
정의 를 참조 할 수 있습니다 . 엔터티에서 해당 주석을 사용하여 결과 쿼리의 페치 계획을 구성 할 수 있습니다. 페치 의 유형 ( Fetch
또는 Load
)은 주석 의 type
속성을 사용하여 구성 할 수 있습니다 @EntityGraph
. 자세한 내용은 JPA 2.1 사양 3.7.4를 참조하십시오.
다음 예제는 엔티티에 이름 지정된 엔티티 그래프를 정의하는 방법을 보여줍니다.
@Entity
@NamedEntityGraph(name = "GroupInfo.detail",
attributeNodes = @NamedAttributeNode("members"))
public class GroupInfo {
// default fetch mode is lazy.
@ManyToMany
List<GroupMember> members = new ArrayList<GroupMember>();
…
}
다음 예제는 저장소 조회 메소드에서 이름 지정된 엔티티 그래프를 참조하는 방법을 보여줍니다.
@Repository
public interface GroupRepository extends CrudRepository<GroupInfo, String> {
@EntityGraph(value = "GroupInfo.detail", type = EntityGraphType.LOAD)
GroupInfo getByGroupName(String name);
}
을 사용하여 임시 엔티티 그래프를 정의 할 수도 있습니다 @EntityGraph
. 제공된 예제는 다음 예와 같이 도메인 유형 에 명시 적으로 추가 할 필요없이 attributePaths
에 맞게 번역됩니다 .EntityGraph
@NamedEntityGraph
@Repository
public interface GroupRepository extends CrudRepository<GroupInfo, String> {
@EntityGraph(attributePaths = { "members" })
GroupInfo getByGroupName(String name);
}
5.3.11. 프로젝션
스프링 데이터 쿼리 메소드는 보통 리포지토리가 관리하는 집합 루트의 하나 또는 여러 인스턴스를 반환합니다. 그러나 때로는 이러한 유형의 특정 속성을 기반으로 투영을 만드는 것이 바람직 할 수 있습니다. Spring Data를 사용하면 전용 리턴 유형을 모델링하여 관리 집계의 부분 뷰를보다 선택적으로 검색 할 수 있습니다.
다음 예제와 같은 저장소 및 집계 루트 유형을 상상하십시오.
class Person {
@Id UUID id;
String firstname, lastname;
Address address;
static class Address {
String zipCode, city, street;
}
}
interface PersonRepository extends Repository<Person, UUID> {
Collection<Person> findByLastname(String lastname);
}
이제 사람의 이름 속성 만 검색하려고한다고 상상해보십시오. Spring Data는이를 달성하기 위해 어떤 수단을 제공합니까? 이 장의 나머지 부분은 그 질문에 대한 답변입니다.
인터페이스 기반 프로젝션
쿼리 결과를 이름 속성으로 만 제한하는 가장 쉬운 방법은 다음 예제와 같이 속성을 읽을 접근 자 메서드를 노출하는 인터페이스를 선언하는 것입니다.
interface NamesOnly {
String getFirstname();
String getLastname();
}
여기서 중요한 점은 여기에 정의 된 속성이 집계 루트의 속성과 정확히 일치한다는 것입니다. 그렇게하면 다음과 같이 쿼리 메소드를 추가 할 수 있습니다.
interface PersonRepository extends Repository<Person, UUID> {
Collection<NamesOnly> findByLastname(String lastname);
}
쿼리 실행 엔진은 반환 된 각 요소에 대해 런타임에 해당 인터페이스의 프록시 인스턴스를 만들고 노출 된 메서드에 대한 호출을 대상 개체에 전달합니다.
투영은 재귀 적으로 사용될 수 있습니다. 일부 Address
정보도 포함 getAddress()
하려면 다음 예제와 같이 해당 투영 인터페이스를 작성하고 선언에서 해당 인터페이스를 리턴 하십시오.
interface PersonSummary {
String getFirstname();
String getLastname();
AddressSummary getAddress();
interface AddressSummary {
String getCity();
}
}
메소드 호출시 address
대상 인스턴스 의 특성을 가져 와서 투영 프록시에 랩핑합니다.
닫힌 투영
접근 자 메서드가 모두 대상 집합의 속성과 일치하는 투영 인터페이스는 닫힌 투영으로 간주됩니다. 다음 예제 (이 장의 앞부분에서도 사용됨)는 닫힌 투영입니다.
interface NamesOnly {
String getFirstname();
String getLastname();
}
닫힌 투영을 사용하는 경우, Spring 프록시는 투영 프록시를 백업하는 데 필요한 모든 속성에 대해 알고 있기 때문에 쿼리 실행을 최적화 할 수 있습니다. 이에 대한 자세한 내용은 참조 설명서의 모듈 별 부분을 참조하십시오.
열린 투영
@Value
다음 예제와 같이 투영 인터페이스의 접근 자 메서드를 사용하여 주석 을 사용하여 새 값을 계산할 수도 있습니다 .
interface NamesOnly {
@Value("#{target.firstname + ' ' + target.lastname}")
String getFullName();
…
}
투영을지지하는 집계 루트는 target
변수 에서 사용할 수 있습니다 . 사용하는 투영 인터페이스 @Value
는 개방형 투영입니다. SpEL 표현식은 집계 루트의 모든 속성을 사용할 수 있으므로이 경우 스프링 데이터는 쿼리 실행 최적화를 적용 할 수 없습니다.
사용되는 표현식 @Value
은 너무 복잡하지 않아야 String
합니다. 변수 프로그래밍을 피하고 싶습니다 . 매우 간단한 표현식의 경우 하나의 옵션은 다음 예제와 같이 기본 메소드 (Java 8에 도입)를 사용하는 것입니다.
interface NamesOnly {
String getFirstname();
String getLastname();
default String getFullName() {
return getFirstname().concat(" ").concat(getLastname());
}
}
이 방법을 사용하려면 투영 인터페이스에 노출 된 다른 접근 자 방법을 기반으로 논리를 순전히 구현할 수 있어야합니다. 보다 유연한 두 번째 옵션은 Spring bean에서 사용자 정의 논리를 구현 한 후 다음 예제와 같이 SpEL 표현식에서이를 호출하는 것입니다.
@Component
class MyBean {
String getFullName(Person person) {
…
}
}
interface NamesOnly {
@Value("#{@myBean.getFullName(target)}")
String getFullName();
…
}
SpEL 표현식 myBean
이 getFullName(…)
메소드를 참조 하고 호출 하고 투영 대상을 메소드 매개 변수로 전달하는 방법에 주목하십시오. SpEL 표현식 평가에 의해 뒷받침되는 메소드는 메소드 매개 변수를 사용할 수 있으며, 이는 표현식에서 참조 될 수 있습니다. 메소드 매개 변수는 Object
이라는 배열을 통해 사용할 수 있습니다 args
. 다음 예제는 args
배열 에서 메소드 매개 변수를 얻는 방법을 보여줍니다 .
interface NamesOnly {
@Value("#{args[0] + ' ' + target.firstname + '!'}")
String getSalutation(String prefix);
}
더 복잡한 표현식의 경우에도 스프링 Bean을 사용하고 앞에서 설명한대로 표현식이 메소드를 호출하도록해야합니다 .
클래스 기반 프로젝션 (DTO)
투영을 정의하는 또 다른 방법은 검색해야하는 필드의 속성을 보유하는 값 형식 DTO (데이터 전송 개체)를 사용하는 것입니다. 이러한 DTO 유형은 프록 싱이 발생하지 않고 중첩 프로젝션을 적용 할 수 없다는 점을 제외하고 프로젝션 인터페이스와 동일한 방식으로 사용할 수 있습니다.
저장소가로드 할 필드를 제한하여 쿼리 실행을 최적화하면로드 할 필드는 노출 된 생성자의 매개 변수 이름에서 결정됩니다.
다음 예는 프로젝션 DTO를 보여줍니다.
class NamesOnly {
private final String firstname, lastname;
NamesOnly(String firstname, String lastname) {
this.firstname = firstname;
this.lastname = lastname;
}
String getFirstname() {
return this.firstname;
}
String getLastname() {
return this.lastname;
}
// equals(…) and hashCode() implementations
}
프로젝션 DTO에 대한 상용구 코드를 피하십시오
주석 을 제공하는 Project Lombok 을 사용하여 DTO의 코드를 크게 단순화 할 수 있습니다
필드는 |
다이내믹 프로젝션
지금까지 투영 유형을 컬렉션의 반환 유형 또는 요소 유형으로 사용했습니다. 그러나 호출시 사용할 유형을 선택하여 동적으로 만들 수 있습니다. 동적 투영을 적용하려면 다음 예에 표시된 것과 같은 쿼리 방법을 사용하십시오.
interface PersonRepository extends Repository<Person, UUID> {
<T> Collection<T> findByLastname(String lastname, Class<T> type);
}
이 방법으로 다음 예제와 같이이 방법을 사용하여 그대로 또는 투영을 적용하여 집계를 얻을 수 있습니다.
void someMethod(PersonRepository people) {
Collection<Person> aggregates =
people.findByLastname("Matthews", Person.class);
Collection<NamesOnly> aggregates =
people.findByLastname("Matthews", NamesOnly.class);
}
Spring Data JPA - Reference DocumentationOliver Gierke,Thomas Darimont,Christoph Strobl,Mark Paluch,Jay Bryant
version 2.3.1.RELEASE,2020-06-10 2.3.1.RELEASE © 2008-2019 The original authors. Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically. |
출처 : https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods