Defining Repository Interfaces

Dev.Hammy·2024년 4월 19일
0

Spring Data JPA

목록 보기
3/13

저장소 인터페이스를 정의하려면 먼저 도메인 클래스별 저장소 인터페이스를 정의해야 합니다. 인터페이스는 Repository를 확장해야 하며 도메인 클래스 및 ID 유형으로 입력되어야 합니다. 해당 도메인 유형에 대한 CRUD 메소드를 노출하려면 Repository 대신 CrudRepository 또는 그 변형 중 하나를 확장할 수 있습니다.

Fine-tuning Repository Definition

저장소 인터페이스를 시작하는 방법에는 몇 가지 변형이 있습니다.

일반적인 접근 방식은 CRUD 기능에 대한 메서드를 제공하는 CrudRepository를 확장하는 것입니다. CRUD는 생성(Create), 읽기(Read), 업데이트(Update), 삭제(Delete)를 의미합니다. 버전 3.0에서는 CrudRepository와 매우 유사한 ListCrudRepository도 도입했지만 여러 엔터티를 반환하는 메서드의 경우 사용하기 더 쉬운 Iterable 대신 List를 반환합니다.

reactive store를 사용하는 경우 사용 중인 반응 프레임워크에 따라 ReactiveCrudRepository 또는 RxJava3CrudRepository를 선택할 수 있습니다.

Kotlin을 사용하는 경우 Kotlin의 코루틴을 활용하는 CoroutineCrudRepository를 선택할 수 있습니다.

추가로 Sort 추상화를 지정하거나 첫 번째 경우 Pageable 추상화를 지정할 수 있는 메서드가 필요한 경우 PagingAndSortingRepository, ReactiveSortingRepository, RxJava3SortingRepository 또는 CoroutineSortingRepository를 확장할 수 있습니다. 다양한 정렬 저장소는 3.0 이전의 Spring Data 버전에서처럼 더 이상 해당 CRUD 저장소를 확장하지 않습니다. 따라서 두 인터페이스의 기능을 모두 사용하려면 두 인터페이스를 모두 확장해야 합니다.

Spring Data 인터페이스를 확장하지 않으려면 @RepositoryDefinition을 사용하여 저장소 인터페이스에 주석을 달 수도 있습니다. CRUD 저장소 인터페이스 중 하나를 확장하면 엔터티를 조작하기 위한 전체 메서드 세트가 노출됩니다. 노출되는 메서드를 선택적으로 선택하려면 CRUD 저장소에서 도메인 저장소로 노출하려는 메서드를 복사하세요. 이때 메소드의 반환 유형을 변경할 수 있습니다. Spring Data는 가능하다면 반환 유형을 존중합니다. 예를 들어 여러 엔터티를 반환하는 메서드의 경우 Iterable<T>, List<T>, Collection<T> 또는 VAVR 목록을 선택할 수 있습니다.

애플리케이션의 많은 저장소에 동일한 메소드 세트가 있어야 하는 경우 상속할 자체 기본 인터페이스를 정의할 수 있습니다. 이러한 인터페이스에는 @NoRepositoryBean으로 annotation을 달아야 합니다. 이렇게 하면 Spring Data가 인스턴스를 직접 생성하려고 시도하고 해당 저장소에 대한 엔터티를 결정할 수 없기 때문에 실패하는 것을 방지할 수 있습니다. 왜냐하면 여전히 generic type 변수가 포함되어 있기 때문입니다.

다음 예에서는 CRUD 메서드(이 경우 findByIdsave)를 선택적으로 노출하는 방법을 보여줍니다.

Selectively exposing CRUD methods

@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를 찾는 쿼리를 실행할 수 있습니다.

[Note]
중간 저장소 인터페이스에는 @NoRepositoryBean이라는 annotation이 붙습니다. Spring Data가 런타임에 인스턴스를 생성하지 말아야 하는 모든 저장소 인터페이스에 해당 annotation을 추가했는지 확인하세요.

Using Repositories with Multiple Spring Data Modules

애플리케이션에서 고유한 Spring Data 모듈을 사용하면 정의된 범위의 모든 저장소 인터페이스가 Spring Data 모듈에 바인딩되므로 작업이 간단해집니다. 때때로 애플리케이션에서는 둘 이상의 Spring Data 모듈을 사용해야 합니다. 이러한 경우 저장소 정의는 지속성 기술을 구별해야 합니다. 클래스 경로에서 여러 저장소 팩토리를 감지하면 Spring Data는 엄격한 저장소 구성 모드로 들어갑니다. 엄격한 구성은 저장소 또는 도메인 클래스에 대한 세부 정보를 사용하여 저장소 정의에 대한 Spring Data 모듈 바인딩을 결정합니다.

  1. 저장소 정의가 모듈별 저장소를 확장하는 경우 이는 특정 Spring Data 모듈에 대한 유효한 후보입니다.

  2. 도메인 클래스에 모듈별 type annotation이 달린 경우 이는 특정 Spring Data 모듈에 대한 유효한 후보입니다. Spring Data 모듈은 제3자 annotation(예: JPA의 @Entity)을 허용하거나 자체 annotation(예: Spring Data MongoDB 및 Spring Data Elasticsearch의 @Document)을 제공합니다.

다음 예에서는 모듈별 인터페이스(이 경우 JPA)를 사용하는 저장소를 보여줍니다.

Example 1. Repository definitions using module-specific interfaces

interface MyRepository extends JpaRepository<User, Long> { }

@NoRepositoryBean
interface MyBaseRepository<T, ID> extends JpaRepository<T, ID> {}

interface UserRepository extends MyBaseRepository<User, Long> {}

MyRepositoryUserRepository는 type 계층 구조에서 JpaRepository를 확장합니다. 이는 Spring Data JPA 모듈의 유효한 후보입니다.

다음 예에서는 generic 인터페이스를 사용하는 저장소를 보여줍니다.

Example 2. Repository definitions using generic interfaces

interface AmbiguousRepository extends Repository<User, Long> {}

@NoRepositoryBean
interface MyBaseRepository<T, ID> extends CrudRepository<T, ID> {}

interface AmbiguousUserRepository extends MyBaseRepository<User, Long> {}

AmbiguousRepositoryAmbiguousUserRepository는 유형 계층 구조에서 RepositoryCrudRepository만 확장합니다. 고유한 Spring Data 모듈을 사용할 때는 괜찮지만 여러 모듈이 이러한 저장소를 바인딩해야 하는 특정 Spring Data를 구별할 수 없습니다.

다음 예에서는 annotation이 포함된 도메인 클래스를 사용하는 저장소를 보여줍니다.

Example 3. Repository definitions using domain classes with annotations

interface PersonRepository extends Repository<Person, Long> {}

@Entity
class Person {}

interface UserRepository extends Repository<User, Long> {}

@Document
class User {}

PersonRepository는 JPA @Entity annotation이 달린 Person을 참조하므로 이 저장소는 분명히 Spring Data JPA에 속합니다. UserRepository는 Spring Data MongoDB의 @Document annotation이 달린 User를 참조합니다.

다음 나쁜 예는 annotation이 혼합된 도메인 클래스를 사용하는 저장소를 보여줍니다.

Example 4. Repository definitions using domain classes with mixed annotations

interface JpaPersonRepository extends Repository<Person, Long> {}

interface MongoDBPersonRepository extends Repository<Person, Long> {}

@Entity
@Document
class Person {}

이 예에서는 JPA 및 Spring Data MongoDB 주석을 모두 사용하는 도메인 클래스를 보여줍니다. JpaPersonRepositoryMongoDBPersonRepository라는 두 개의 저장소를 정의합니다. 하나는 JPA용이고 다른 하나는 MongoDB용입니다. Spring Data는 더 이상 리포지토리를 구분할 수 없으므로 정의되지 않은 동작이 발생합니다.

특정 Spring Data 모듈에 대한 저장소 후보를 식별하기 위한 엄격한 저장소 구성에 저장소 type 세부사항구별되는 도메인 클래스 annotation이 사용됩니다. 동일한 도메인 유형에 여러 영속성 기술별 annotation을 사용하는 것이 가능하며 여러 영속성 기술에서 도메인 type을 재사용할 수 있습니다. 그러나 Spring Data는 더 이상 저장소를 바인딩할 고유 모듈을 결정할 수 없습니다.

저장소를 구별하는 마지막 방법은 저장소 기본 패키지의 범위를 지정하는 것입니다. base package는 저장소 인터페이스 정의를 검색하기 위한 시작점을 정의합니다. 이는 저장소 정의가 적절한 패키지에 있다는 것을 의미합니다. 기본적으로 anootation 기반 구성은 configuration 클래스 패키지를 사용합니다. XML 기반 구성의 base package는 필수입니다.

다음 예에서는 base package의 annotation 기반 구성을 보여줍니다.

Annotation-driven configuration of base packages

@EnableJpaRepositories(basePackages = "com.acme.repositories.jpa")
@EnableMongoRepositories(basePackages = "com.acme.repositories.mongo")
class Configuration {}

0개의 댓글