4.3. 리포지토리 인터페이스 정의
먼저 도메인 클래스 별 저장소 인터페이스를 정의하십시오. 인터페이스는 Repository
도메인 클래스와 ID 유형으로 확장 되고 입력 되어야합니다 . 해당 도메인 유형에 대한 CRUD 메소드를 노출하려면 CrudRepository
대신 확장하십시오 Repository
.
4.3.1. 미세 조정 리포지토리 정의
일반적으로 리포지토리 인터페이스 Repository
는 CrudRepository
, 또는로 확장됩니다 PagingAndSortingRepository
. 또는 스프링 데이터 인터페이스를 확장하지 않으려면로 저장소 인터페이스에 주석을 달 수도 있습니다 @RepositoryDefinition
. 확장 CrudRepository
하면 엔터티를 조작하기위한 전체 메서드 집합이 노출됩니다. 노출되는 메소드를 선택적으로 선택하려면 노출하려는 메소드를 CrudRepository
도메인 저장소로 복사하십시오 .
그렇게하면 제공된 스프링 데이터 리포지토리 기능 위에 자신의 추상화를 정의 할 수 있습니다. |
다음 예제는 CRUD 메소드를 선택적으로 노출하는 방법을 보여줍니다 ( 이 경우 findById
및 save
).
@NoRepositoryBean
interface MyBaseRepository<T, ID> extends Repository<T, ID> {
Optional<T> findById(ID id);
<S extends T> S save(S entity);
}
interface UserRepository extends MyBaseRepository<User, Long> {
User findByEmailAddress(EmailAddress emailAddress);
}
이전 예제에서는 모든 도메인 리포지토리에 대한 공통 기본 인터페이스를 정의하고 공개 findById(…)
했습니다 save(…)
. 이러한 메소드는 Spring Data에서 제공하는 선택한 저장소의 기본 리포지토리 구현으로 라우팅됩니다 (예 : JPA를 사용하는 경우, 의 SimpleJpaRepository
메소드 서명과 일치하기 때문에 구현은 )입니다 CrudRepository
. 은 그래서 UserRepository
지금은 사용자가 저장, ID에 의해 개별 사용자를 발견하고 찾을 수있는 쿼리를 실행할 수있는 Users
이메일 주소를.
중간 저장소 인터페이스에는로 주석이 달립니다 @NoRepositoryBean . 스프링 데이터가 런타임에 인스턴스를 작성하지 않아야하는 모든 저장소 인터페이스에 해당 주석을 추가하십시오. |
4.3.2. 여러 스프링 데이터 모듈과 함께 저장소 사용
정의 된 범위의 모든 저장소 인터페이스가 스프링 데이터 모듈에 바인딩되어 있기 때문에 애플리케이션에서 고유 한 스프링 데이터 모듈을 사용하면 작업이 단순 해집니다. 때때로, 어플리케이션은 하나 이상의 스프링 데이터 모듈을 사용해야합니다. 이러한 경우, 저장소 정의는 지속성 기술을 구별해야합니다. 클래스 경로에서 여러 저장소 팩토리를 감지하면 Spring Data는 엄격한 저장소 구성 모드로 들어갑니다. 엄격한 구성은 저장소 또는 도메인 클래스의 세부 사항을 사용하여 저장소 정의에 대한 스프링 데이터 모듈 바인딩을 결정합니다.
- 리포지토리 정의 가 모듈 특정 리포지토리를 확장 하면 특정 스프링 데이터 모듈의 유효한 후보입니다.
- 도메인 클래스 에 모듈 별 유형 주석 이 주석 처리 된 경우 특정 스프링 데이터 모듈의 유효한 후보입니다. Spring Data 모듈은 타사 주석 (예 : JPA
@Entity
)을 받거나 자체 주석을 제공합니다 (예 :@Document
Spring Data MongoDB 및 Spring Data Elasticsearch).
다음 예제는 모듈 특정 인터페이스 (이 경우 JPA)를 사용하는 저장소를 보여줍니다.
interface MyRepository extends JpaRepository<User, Long> { }
@NoRepositoryBean
interface MyBaseRepository<T, ID> extends JpaRepository<T, ID> { … }
interface UserRepository extends MyBaseRepository<User, Long> { … }
MyRepository
및 UserRepository
확장 JpaRepository
자신의 유형 계층 구조에서. Spring Data JPA 모듈의 유효한 후보입니다.
다음 예제는 일반 인터페이스를 사용하는 저장소를 보여줍니다.
interface AmbiguousRepository extends Repository<User, Long> { … }
@NoRepositoryBean
interface MyBaseRepository<T, ID> extends CrudRepository<T, ID> { … }
interface AmbiguousUserRepository extends MyBaseRepository<User, Long> { … }
AmbiguousRepository
및 AmbiguousUserRepository
단지 확장 Repository
하고 CrudRepository
자신의 유형 계층 구조에서. 고유 한 Spring Data 모듈을 사용할 때 이것은 완벽하지만 여러 모듈은 이러한 리포지토리를 바인딩 할 특정 Spring Data와 구별 할 수 없습니다.
다음 예제는 주석이있는 도메인 클래스를 사용하는 저장소를 보여줍니다.
interface PersonRepository extends Repository<Person, Long> { … }
@Entity
class Person { … }
interface UserRepository extends Repository<User, Long> { … }
@Document
class User { … }
PersonRepository
참고 문헌 Person
은 JPA @Entity
주석으로 주석 이 달려 있으므로이 저장소는 Spring Data JPA에 속한다. UserRepository
참고 User
자료는 Spring Data MongoDB의 @Document
주석으로 주석이 달린다 .
다음 잘못된 예는 주석이 혼합 된 도메인 클래스를 사용하는 저장소를 보여줍니다.
interface JpaPersonRepository extends Repository<Person, Long> { … }
interface MongoDBPersonRepository extends Repository<Person, Long> { … }
@Entity
@Document
class Person { … }
이 예제는 JPA 및 Spring Data MongoDB 주석을 모두 사용하는 도메인 클래스를 보여줍니다. 그것은 두 개의 저장소를 정의 JpaPersonRepository
하고 MongoDBPersonRepository
. 하나는 JPA를위한 것이고 다른 하나는 MongoDB 사용을위한 것입니다. 스프링 데이터는 더 이상 리포지토리를 구분할 수 없으므로 정의되지 않은 동작이 발생합니다.
저장소 유형 세부 사항 및 구별 도메인 클래스 주석은 엄격한 저장소 구성에 사용되어 특정 Spring Data 모듈의 저장소 후보를 식별합니다. 동일한 도메인 유형에서 다중 지속성 기술 특정 어노테이션을 사용할 수 있으며 다중 지속성 기술에서 도메인 유형을 재사용 할 수 있습니다. 그러나 스프링 데이터는 더 이상 리포지토리를 바인딩 할 고유 모듈을 결정할 수 없습니다.
리포지토리를 구분하는 마지막 방법은 리포지토리 기본 패키지의 범위를 지정하는 것입니다. 기본 패키지는 저장소 인터페이스 정의를 스캔하기위한 시작점을 정의하며, 이는 적절한 패키지에 저장소 정의가 있음을 의미합니다. 기본적으로 주석 기반 구성은 구성 클래스의 패키지를 사용합니다. XML 기반 구성 의 기본 패키지 는 필수입니다.
다음 예제는 주석 기반의 기본 패키지 구성을 보여줍니다.
@EnableJpaRepositories(basePackages = "com.acme.repositories.jpa")
@EnableMongoRepositories(basePackages = "com.acme.repositories.mongo")
class Configuration { … }
4.4. 쿼리 방법 정의
저장소 프록시에는 메소드 이름에서 상점 특정 조회를 파생시키는 두 가지 방법이 있습니다.
- 메서드 이름에서 직접 쿼리를 파생시킵니다.
- 수동으로 정의 된 쿼리를 사용합니다.
사용 가능한 옵션은 실제 상점에 따라 다릅니다. 그러나 어떤 실제 쿼리를 만들지 결정하는 전략이 있어야합니다. 다음 섹션에서는 사용 가능한 옵션에 대해 설명합니다.
4.4.1. 쿼리 조회 전략
리포지토리 인프라에서 쿼리를 해결하기 위해 다음 전략을 사용할 수 있습니다. XML 구성을 사용하면 query-lookup-strategy
속성을 통해 네임 스페이스에서 전략을 구성 할 수 있습니다 . Java 구성 queryLookupStrategy
의 경우 Enable${store}Repositories
주석 의 속성을 사용할 수 있습니다 . 특정 전략에 따라 일부 전략이 지원되지 않을 수 있습니다.
CREATE
조회 메소드 이름에서 상점 특정 조회를 구성하려고 시도합니다. 일반적인 접근 방식은 메소드 이름에서 주어진 잘 알려진 접두어 세트를 제거하고 나머지 메소드를 구문 분석하는 것입니다. 쿼리 생성에 대한 자세한 내용은“ 쿼리 생성 ”을 참조하십시오.USE_DECLARED_QUERY
선언 된 쿼리를 찾으려고 시도 할 수 없으면 예외를 던집니다. 쿼리는 어딘가에 주석으로 정의하거나 다른 방법으로 선언 할 수 있습니다. 해당 상점에 사용 가능한 옵션을 찾으려면 특정 상점의 문서를 참조하십시오. 부트 스트랩 시간에 저장소 인프라가 메소드에 대해 선언 된 쿼리를 찾지 못하면 실패합니다.CREATE_IF_NOT_FOUND
(기본값)CREATE
과 와를 결합USE_DECLARED_QUERY
합니다. 선언 된 쿼리를 먼저 조회하고 선언 된 쿼리가 없으면 사용자 정의 메소드 이름 기반 쿼리를 작성합니다. 이것이 기본 조회 전략이므로 명시 적으로 아무것도 구성하지 않으면 사용됩니다. 메소드 이름으로 빠른 쿼리 정의를 허용 할뿐만 아니라 필요에 따라 선언 된 쿼리를 도입하여 이러한 쿼리를 사용자 정의 튜닝 할 수도 있습니다.
4.4.2. 쿼리 생성
스프링 데이터 리포지토리 인프라에 내장 된 쿼리 빌더 메커니즘은 리포지토리 엔터티에 대한 제한 쿼리를 작성하는 데 유용합니다. 메커니즘은 접두사 스트립 find…By
, read…By
, query…By
, count…By
, 및 get…By
그것의 나머지 부분을 파싱 방법 및 시작. 도입 절은 Distinct
작성 될 조회에서 구별 플래그를 설정하는 등의 추가 표현식을 포함 할 수 있습니다 . 그러나 첫 번째 By
는 실제 기준의 시작을 나타내는 구분 기호 역할을합니다. 아주 기본적인 수준에서 개체 속성에 대한 조건을 정의하고 그들과 함께 연결할 And
및 Or
. 다음 예제는 여러 쿼리를 작성하는 방법을 보여줍니다.
interface PersonRepository extends Repository<Person, Long> {
List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);
// Enables the distinct flag for the query
List<Person> findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname);
List<Person> findPeopleDistinctByLastnameOrFirstname(String lastname, String firstname);
// Enabling ignoring case for an individual property
List<Person> findByLastnameIgnoreCase(String lastname);
// Enabling ignoring case for all suitable properties
List<Person> findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname);
// Enabling static ORDER BY for a query
List<Person> findByLastnameOrderByFirstnameAsc(String lastname);
List<Person> findByLastnameOrderByFirstnameDesc(String lastname);
}
메소드 구문 분석의 실제 결과는 조회를 작성하는 지속성 저장소에 따라 다릅니다. 그러나 주목해야 할 몇 가지 일반적인 사항이 있습니다.
- 표현식은 일반적으로 연결할 수있는 연산자와 결합 된 속성 순회입니다. 당신과 함께 속성 식을 결합 할 수 있습니다
AND
와OR
. 당신은 또한 다음과 같은 사업자에 대한 지원받을Between
,LessThan
,GreaterThan
, 및Like
속성 표현식을. 지원되는 연산자는 데이터 스토어마다 다를 수 있으므로 참조 문서의 해당 부분을 참조하십시오. - 메소드 구문 분석기는
IgnoreCase
개별 특성 (예findByLastnameIgnoreCase(…)
:) 또는 대소 문자 무시 (일반적으로String
예를 들어findByLastnameAndFirstnameAllIgnoreCase(…)
) 를 무시하는 유형의 모든 특성에 대한 플래그 설정 을 지원합니다 . 사례를 무시할지 여부는 상점마다 다를 수 있으므로 상점 별 조회 방법에 대해서는 참조 문서의 관련 섹션을 참조하십시오. OrderBy
속성을 참조하는 쿼리 메서드에 절을 추가 하고 정렬 방향 (Asc
또는Desc
) 을 제공하여 정적 순서를 적용 할 수 있습니다 . 동적 정렬을 지원하는 쿼리 방법을 만들려면“ 특수 매개 변수 처리 ”를 참조하십시오 .
4.4.3. 속성 표현
앞의 예에서와 같이 속성 식은 관리 대상 엔터티의 직접 속성 만 참조 할 수 있습니다. 쿼리 작성시 구문 분석 된 특성이 관리되는 도메인 클래스의 특성인지 확인하십시오. 그러나 중첩 속성을 통과하여 제약 조건을 정의 할 수도 있습니다. 다음 메소드 서명을 고려하십시오.
List<Person> findByAddressZipCode(ZipCode zipCode);
A는 가정 Person
갖는 Address
A의이 ZipCode
. 이 경우, 메소드는 특성 traversal을 작성합니다 x.address.zipCode
. 확인 알고리즘은 전체 부분 ( AddressZipCode
)을 속성 으로 해석하여 시작 하고 도메인 클래스에서 해당 이름을 가진 속성 (자본이없는)을 확인합니다. 알고리즘이 성공하면 해당 속성을 사용합니다. 그렇지 않은 경우 알고리즘은 낙타 케이스 부분의 소스를 오른쪽에서 머리와 꼬리로 분할하고 해당 속성 (예 : AddressZip
및) 을 찾습니다 Code
. 알고리즘이 해당 헤드가있는 특성을 찾으면 꼬리를 가져 와서 위에서 아래로 트리를 작성하여 위에서 설명한 방식으로 꼬리를 나눕니다. 첫 번째 분할이 일치하지 않으면 알고리즘이 분할 지점을 왼쪽으로 이동합니다 ( Address
,ZipCode
) 계속합니다.
대부분의 경우이 방법이 작동하지만 알고리즘이 잘못된 속성을 선택할 수 있습니다. Person
클래스에 addressZip
속성 이 있다고 가정하십시오 . 알고리즘은 이미 첫 번째 스플릿 라운드에서 일치하고 잘못된 속성을 선택하고 실패합니다 ( addressZip
아마도 유형이 code
속성 이 없기 때문에 ).
이 모호성을 해결하기 위해 _
메소드 이름 내부 를 사용 하여 순회 지점을 수동으로 정의 할 수 있습니다 . 따라서 메소드 이름은 다음과 같습니다.
List<Person> findByAddress_ZipCode(ZipCode zipCode);
밑줄 문자를 예약 문자로 취급하기 때문에 표준 Java 명명 규칙 (속성 이름에 밑줄을 사용하지 않고 대신 낙타 문자를 사용)을 따르는 것이 좋습니다.
4.4.4. 특수 매개 변수 처리
쿼리에서 매개 변수를 처리하려면 앞의 예제에서 이미 본 것처럼 메서드 매개 변수를 정의하십시오. 또한 인프라는 Pageable
and와 같은 특정 유형을 인식 Sort
하여 쿼리에 페이지 매김 및 정렬을 동적으로 적용합니다. 다음 예제는 이러한 기능을 보여줍니다.
Pageable
, Slice
및 Sort
쿼리 메소드에서Page<User> findByLastname(String lastname, Pageable pageable);
Slice<User> findByLastname(String lastname, Pageable pageable);
List<User> findByLastname(String lastname, Sort sort);
List<User> findByLastname(String lastname, Pageable pageable);
복용하는 API Sort 및 Pageable 비 기대 null 값이 방법으로 건네 될 수 있습니다. 정렬 또는 페이지 매김을 사용하지 않으려면 Sort.unsorted() 및을 사용하십시오 Pageable.unpaged() . |
첫 번째 방법을 사용하면 org.springframework.data.domain.Pageable
인스턴스를 쿼리 방법으로 전달하여 정적으로 정의 된 쿼리에 페이징을 동적으로 추가 할 수 있습니다. A Page
는 사용 가능한 총 요소 및 페이지 수에 대해 알고 있습니다. 인프라가 전체 쿼리를 계산하기 위해 카운트 쿼리를 트리거하여이를 수행합니다. 사용 된 상점에 따라 비용이 많이들 수 있으므로 대신을 반환 할 수 있습니다 Slice
. A Slice
는 다음 Slice
을 사용할 수 있는지 여부 만 알며 이는 더 큰 결과 집합을 걸을 때 충분할 수 있습니다.
정렬 옵션도 Pageable
인스턴스를 통해 처리됩니다 . 정렬 만 필요한 경우 org.springframework.data.domain.Sort
분석법에 매개 변수를 추가 하십시오. 보시다시피, 반환 List
도 가능합니다. 이 경우 실제 Page
인스턴스를 작성 하는 데 필요한 추가 메타 데이터 가 작성되지 않습니다 (따라서 필요한 추가 카운트 조회가 발행되지 않음을 의미 함). 오히려 쿼리가 주어진 범위의 엔터티 만 찾도록 제한합니다.
전체 쿼리에 대한 페이지 수를 확인하려면 추가 카운트 쿼리를 트리거해야합니다. 기본적으로이 쿼리는 실제로 트리거 한 쿼리에서 파생됩니다. |
페이징 및 정렬
속성 이름을 사용하여 간단한 정렬 식을 정의 할 수 있습니다. 여러 기준을 하나의 표현식으로 수집하기 위해 표현식을 연결할 수 있습니다.
Sort sort = Sort.by("firstname").ascending()
.and(Sort.by("lastname").descending());
정렬 식을 정의하는보다 안전한 형식의 방법을 보려면 형식으로 시작하여 정렬 식을 정의하고 메서드 참조를 사용하여 정렬 할 속성을 정의하십시오.
TypedSort<Person> person = Sort.sort(Person.class);
TypedSort<Person> sort = person.by(Person::getFirstname).ascending()
.and(person.by(Person::getLastname).descending());
상점 구현이 Querydsl을 지원하는 경우 생성 된 메타 모델 유형을 사용하여 정렬 표현식을 정의 할 수도 있습니다.
QSort sort = QSort.by(QPerson.firstname.asc())
.and(QSort.by(QPerson.lastname.desc()));
4.4.5. 쿼리 결과 제한
first
또는 top
키워드 를 사용하여 쿼리 방법의 결과를 제한 할 수 있습니다. 선택적 숫자 값을 추가 top
하거나 first
반환 할 최대 결과 크기를 지정할 수 있습니다 . 숫자를 생략하면 결과 크기는 1로 가정됩니다. 다음 예제는 쿼리 크기를 제한하는 방법을 보여줍니다.
Top
and 를 사용하여 쿼리 결과 크기 제한First
User findFirstByOrderByLastnameAsc();
User findTopByOrderByAgeDesc();
Page<User> queryFirst10ByLastname(String lastname, Pageable pageable);
Slice<User> findTop3ByLastname(String lastname, Pageable pageable);
List<User> findFirst10ByLastname(String lastname, Sort sort);
List<User> findTop10ByLastname(String lastname, Pageable pageable);
제한 표현식도 Distinct
키워드를 지원합니다 . 또한 결과 집합을 하나의 인스턴스로 제한하는 쿼리의 경우 Optional
키워드 로 결과를 래핑하는 것이 지원됩니다.
페이지 매김 또는 슬라이싱이 제한 쿼리 페이지 매김 (및 사용 가능한 페이지 수 계산)에 적용되는 경우 제한된 결과 내에 적용됩니다.
Sort 매개 변수를 사용 하여 결과를 동적 정렬과 함께 제한하면 'K'가장 작은 요소와 'K'가장 큰 요소에 대한 쿼리 방법을 표현할 수 있습니다. |
4.4.6. 컬렉션 또는 이터 러블을 반환하는 리포지토리 메서드
복수의 결과는 표준 자바 사용할 수 있습니다 반환 쿼리 방법 Iterable
, List
, Set
. 그 외에도 우리 는 Vavr이 제공하는 컬렉션 유형뿐만 아니라 Streamable
커스텀 확장 인 Spring Data를 반환하는 것을 지원 합니다.Iterable
쿼리 가능 반환 형식으로 Streamable 사용
Streamable
대안으로 Iterable
또는 모든 컬렉션 유형 으로 사용할 수 있습니다 . 그것은 평행하지 않은 Stream
(로부터 누락 된 Iterable
) 액세스, 편리한 요소를 통해 요소 를 직접 ….filter(…)
및 다른 ….map(…)
요소에 연결하는 기능 Streamable
을 제공합니다.
interface PersonRepository extends Repository<Person, Long> {
Streamable<Person> findByFirstnameContaining(String firstname);
Streamable<Person> findByLastnameContaining(String lastname);
}
Streamable<Person> result = repository.findByFirstnameContaining("av")
.and(repository.findByLastnameContaining("ea"));
사용자 정의 스트리밍 가능 랩퍼 유형 리턴
컬렉션에 전용 래퍼 유형을 제공하는 것은 여러 요소를 반환하는 쿼리 실행 결과에 API를 제공하기 위해 일반적으로 사용되는 패턴입니다. 일반적으로 이러한 유형은 콜렉션과 같은 유형을 리턴하고 랩퍼 유형의 인스턴스를 수동으로 작성하는 저장소 메소드를 호출하여 사용됩니다. 스프링 데이터가 다음 기준을 충족하는 경우 이러한 래퍼 유형을 쿼리 메소드 리턴 유형으로 사용할 수 있으므로 추가 단계를 피할 수 있습니다.
- 유형은을 구현
Streamable
합니다. - 이 형식은 생성자 또는 명명 된 인수
of(…)
또는valueOf(…)
인수Streamable
로 사용 되는 정적 팩토리 메소드를 노출합니다 .
샘플 사용 사례는 다음과 같습니다.
class Product { (1)
MonetaryAmount getPrice() { … }
}
@RequiredArgConstructor(staticName = "of")
class Products implements Streamable<Product> { (2)
private Streamable<Product> streamable;
public MonetaryAmount getTotal() { (3)
return streamable.stream() //
.map(Priced::getPrice)
.reduce(Money.of(0), MonetaryAmount::add);
}
}
interface ProductRepository implements Repository<Product, Long> {
Products findAllByDescriptionContaining(String text); (4)
}
1 | Product 제품 가격에 액세스하기 위해 API를 노출 하는 엔티티입니다. |
2 | Streamable<Product> 를 통해 구성 할 수있는 래퍼 유형입니다 Products.of(…) (롬복 주석을 통해 생성 된 팩토리 메소드). |
3 | 래퍼 유형은에서 새로운 값을 계산하는 추가 API를 노출합니다 Streamable<Product> . |
4 | 이 랩퍼 유형은 조회 메소드 리턴 유형으로 직접 사용될 수 있습니다. Stremable<Product> 리포지토리 클라이언트에서 반환 하고 수동으로 포장 할 필요가 없습니다 . |
Vavr 컬렉션 지원
Vavr 은 Java의 기능적 프로그래밍 개념을 수용하는 라이브러리입니다. 쿼리 메소드 리턴 유형으로 사용할 수있는 사용자 정의 콜렉션 유형 세트와 함께 제공됩니다.
Vavr 수집 유형 | 사용 된 Vavr 구현 유형 | 유효한 Java 소스 유형 |
---|---|---|
|
|
|
|
|
|
|
|
|
첫 번째 열의 유형 (또는 하위 유형)은 quer 메소드 리턴 유형으로 사용될 수 있으며 실제 쿼리 결과의 Java 유형 (세 번째 열)에 따라 구현 유형으로 사용되는 두 번째 열의 유형을 가져옵니다. 또는 Traversable
(Vavr에 Iterable
상응하는)를 선언하고 실제 반환 값에서 구현 클래스를 파생합니다. 즉 a java.util.List
는 Vavr List
/ 으로 바뀌고 Seq
a java.util.Set
는 Vavr LinkedHashSet
/ Set
등 이됩니다 .
4.4.7. 리포지토리 방법의 널 처리
Spring Data 2.0부터 개별 집계 인스턴스를 리턴하는 저장소 CRUD 메소드는 Java 8을 사용 Optional
하여 값이 없을 가능성을 표시합니다. 그 외에도 Spring Data는 쿼리 메소드에서 다음 래퍼 유형 반환을 지원합니다.
com.google.common.base.Optional
scala.Option
io.vavr.control.Option
또는 쿼리 메소드가 랩퍼 유형을 전혀 사용하지 않도록 선택할 수 있습니다. 쿼리 결과가 없으면를 반환하여 나타냅니다 null
. 컬렉션, 컬렉션 대안, 래퍼 및 스트림을 반환하는 리포지토리 메서드는 반환 null
되지 않고 해당하는 빈 표현 을 보장 합니다. 자세한 내용은“ 리포지토리 쿼리 반환 유형 ”을 참조하십시오.
무효 성 주석
Spring Framework의 Null 허용 주석 을 사용하여 리포지토리 메서드에 대한 Null 허용 제약 조건을 표현할 수 있습니다 . null
다음과 같이 런타임 중 도구 친화적 인 접근 방식과 옵트 인 확인 기능을 제공합니다.
@NonNullApi
: 패키지 수준에서 매개 변수 및 반환 값의 기본 동작이 값을 수락하거나 생성하지 않음을 선언하는 데 사용null
됩니다.@NonNull
: 매개 변수 또는 반환 값에 사용되지 않아야합니다null
( 해당 되는 경우 매개 변수 및 반환 값에는 필요하지 않음@NonNullApi
).@Nullable
: 가능한 매개 변수 또는 반환 값에 사용됩니다null
.
스프링 어노테이션은 JSR 305 어노테이션 (휴면하지만 널리 퍼진 JSR) 으로 메타 어노테이션됩니다 . JSR 305 메타 주석을 사용하면 IDEA , Eclipse 및 Kotlin 과 같은 툴링 벤더 가 스프링 주석에 대한 지원을 하드 코딩하지 않고도 일반적인 방식으로 널 안전 지원을 제공 할 수 있습니다. 쿼리 방법에 대한 nullability 제약 조건의 런타임 검사를 활성화 하려면 다음 예제와 같이 Spring @NonNullApi
in 을 사용하여 패키지 수준에서 null이 아닌 속성을 활성화해야합니다 package-info.java
.
package-info.java
@org.springframework.lang.NonNullApi
package com.acme;
널이 아닌 기본값이 설정되면 런타임시 저장소 조회 메소드 호출이 널 입력 가능 제한 조건에 대해 유효성 검증됩니다. 쿼리 실행 결과가 정의 된 제약 조건을 위반하면 예외가 발생합니다. 이것은 메소드가 리턴 null
하지만 널 입력 불가능으로 선언 될 때 발생 합니다 (저장소가 상주하는 패키지에 정의 된 주석이있는 기본값). 널 입력 가능 결과에 다시 옵트 인하려면 @Nullable
개별 메소드 에서 선택적으로 사용하십시오 . 이 섹션의 시작 부분에 언급 된 결과 랩퍼 유형을 사용하면 예상대로 계속 작동합니다. 빈 결과는 부재를 나타내는 값으로 변환됩니다.
다음 예제는 방금 설명한 여러 기술을 보여줍니다.
package com.acme; (1)
import org.springframework.lang.Nullable;
interface UserRepository extends Repository<User, Long> {
User getByEmailAddress(EmailAddress emailAddress); (2)
@Nullable
User findByEmailAddress(@Nullable EmailAddress emailAdress); (3)
Optional<User> findOptionalByEmailAddress(EmailAddress emailAddress); (4)
}
1 | 저장소는 null이 아닌 동작을 정의한 패키지 (또는 하위 패키지)에 있습니다. |
2 | EmptyResultDataAccessException 쿼리가 실행되어 결과가 생성되지 않으면를 던집니다 . 예외 IllegalArgumentException 때 emailAddress 방법에 손입니다 null . |
3 | null 쿼리가 결과를 생성하지 않을 때 반환 합니다. null 의 값으로 도 허용 됩니다 emailAddress . |
4 | Optional.empty() 쿼리가 결과를 생성하지 않을 때 반환 합니다. 예외 IllegalArgumentException 때 emailAddress 방법에 손입니다 null . |
코 틀린 기반 리포지토리의 Nullability
Kotlin은 언어에 적용되는 Null 허용 제약 조건을 정의했습니다 . Kotlin 코드는 바이트 코드로 컴파일되는데,이 메서드는 메서드 서명을 통한 nullable 제약 조건이 아니라 컴파일 된 메타 데이터를 통해 표시됩니다. kotlin-reflect
Kotlin의 Null 허용 제한 조건을 검토 할 수 있도록 프로젝트에 JAR 을 포함시켜야합니다 . 스프링 데이터 리포지토리는 언어 메커니즘을 사용하여 다음과 같이 동일한 런타임 검사를 적용하기 위해 이러한 제약 조건을 정의합니다.
interface UserRepository : Repository<User, String> {
fun findByUsername(username: String): User (1)
fun findByFirstname(firstname: String?): User? (2)
}
1 | 이 메소드는 매개 변수와 결과를 널 입력 불가능으로 정의합니다 (Kotlin 기본값). Kotlin 컴파일러 null 는 메소드에 전달 되는 메소드 호출을 거부합니다 . 쿼리 실행에서 빈 결과 EmptyResultDataAccessException 가 나오면가 발생합니다. |
2 | 이 방법은 받아 null 에 대한 firstname 매개 변수 및 반환 null 쿼리 실행이 결과를 생성하지 않는 경우. |
4.4.8. 스트리밍 쿼리 결과
Java 8 Stream<T>
을 리턴 유형으로 사용하여 조회 메소드의 결과를 증분 처리 할 수 있습니다 . 쿼리 결과를 래핑하는 대신 Stream
다음 예제와 같이 데이터 저장소 별 방법을 사용하여 스트리밍을 수행합니다.
Stream<T>
@Query("select u from User u")
Stream<User> findAllByCustomQueryAndStream();
Stream<User> readAllByFirstnameNotNull();
@Query("select u from User u")
Stream<User> streamAllPaged(Pageable pageable);
A는 Stream 가능성이 있으므로, 사용 후 폐쇄, 기본 데이터 저장 관련 자원과 반드시 랩합니다. 다음 예제와 같이 메소드를 사용하거나 Java 7 블록 을 사용 Stream 하여 수동으로 닫을 수 있습니다 . close() try-with-resources |
Stream<T>
자원에 대한 시도 블록 의 결과에 대한 작업try (Stream<User> stream = repository.findAllByCustomQueryAndStream()) {
stream.forEach(…);
}
현재 모든 스프링 데이터 모듈 Stream<T> 이 리턴 유형으로 지원 되는 것은 아닙니다 . |
4.4.9. 비동기 쿼리 결과
Spring의 비동기 메소드 실행 기능 을 사용하여 리포지토리 쿼리를 비동기 적으로 실행할 수 있습니다 . 이것은 실제 쿼리 실행이 Spring에 제출 된 작업에서 발생하는 동안 메소드가 호출하자마자 반환한다는 것을 의미합니다 TaskExecutor
. 비동기 쿼리 실행은 반응 쿼리 실행과 다르며 혼합해서는 안됩니다. 사후 대응 지원에 대한 자세한 내용은 상점 별 설명서를 참조하십시오. 다음 예제는 여러 비동기 쿼리를 보여줍니다.
@Async
Future<User> findByFirstname(String firstname); (1)
@Async
CompletableFuture<User> findOneByFirstname(String firstname); (2)
@Async
ListenableFuture<User> findOneByLastname(String lastname); (3)
1 | java.util.concurrent.Future 반환 유형으로 사용하십시오 . |
2 | java.util.concurrent.CompletableFuture 리턴 유형으로 Java 8 을 사용하십시오 . |
3 | org.springframework.util.concurrent.ListenableFuture 반환 유형으로 a 를 사용하십시오 . |
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. |
출처 : docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.definition
'IT > Spring-Data-JPA' 카테고리의 다른 글
스프링 데이터 JPA #Reference:지속 엔티티 (0) | 2020.06.19 |
---|---|
스프링 데이터 JPA #Intro:레포지터리 작업 (4) (0) | 2020.06.19 |
스프링 데이터 JPA #Intro:레포지터리 작업 (3) (0) | 2020.06.19 |
스프링 데이터 JPA #Intro:레포지터리 작업 (1) (0) | 2020.06.19 |
스프링 데이터 JPA #Intro (0) | 2020.06.19 |