1. 소개
실제 시스템이나 프로세스를 모델링할 때 DDD(도메인 기반 디자인) 스타일 리포지토리는 좋은 옵션입니다. 이를 위해 Spring Data JPA를 데이터 액세스 추상화 계층으로 사용할 수 있습니다.
이 개념을 처음 사용하는 경우 속도를 높이는 데 도움 이 되는 이 소개 사용방법(예제) 를 확인하십시오.
이 사용방법(예제)에서는 프래그먼트라고 하는 더 작은 리포지토리를 사용하여 생성되는 구성 가능한 리포지토리 및 사용자 지정 생성 개념에 중점을 둘 것입니다.
2. 메이븐 의존성
구성 가능한 리포지토리를 생성하는 옵션은 Spring 5부터 사용할 수 있습니다.
Spring Data JPA에 필요한 의존성을 추가해 보겠습니다.
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
또한 데이터 액세스 계층이 작동하려면 데이터 원본을 설정해야 합니다. 개발 및 빠른 테스트를 위해 H2와 같은 메모리 내 데이터베이스를 설정 하는 것이 좋습니다 .
3. 배경
3.1. JPA 구현으로 Hibernate
Spring Data JPA는 기본적으로 Hibernate를 JPA 구현으로 사용합니다. 우리는 쉽게 서로 혼동하거나 비교할 수 있지만 서로 다른 목적을 수행합니다.
Spring Data JPA는 모든 구현을 사용할 수 있는 데이터 액세스 추상화 계층입니다. 예를 들어 EclipseLink를 위해 Hibernate를 전환 할 수 있습니다.
3.2. 기본 저장소
대부분의 경우 쿼리를 직접 작성할 필요가 없습니다.
대신 일반 Spring 데이터 저장소 인터페이스를 차례로 확장하는 인터페이스만 생성하면 됩니다.
public interface LocationRepository extends JpaRepository<Location, Long> {
}
그리고 이것은 그 자체로 Long 유형의 기본 키가 있는 Location 개체 에서 CRUD, 페이징 및 정렬과 같은 일반적인 작업을 수행할 수 있도록 합니다 .
또한 Spring Data JPA에는 메서드 이름 규칙을 사용하여 쿼리를 생성하는 기능을 제공하는 쿼리 빌더 메커니즘이 장착되어 있습니다.
public interface StoreRepository extends JpaRepository<Store, Long> {
List<Store> findStoreByLocationId(Long locationId);
}
3.3. 사용자 지정 저장소
필요한 경우 프래그먼트 인터페이스를 작성하고 원하는 기능을 구현하여 모델 리포지토리를 보강 할 수 있습니다. 그런 다음 자체 JPA 저장소에 주입할 수 있습니다.
예를 들어 여기 에서는 조각 저장소를 확장하여 ItemTypeRepository 를 보강하고 있습니다.
public interface ItemTypeRepository
extends JpaRepository<ItemType, Long>, CustomItemTypeRepository {
}
여기서 CustomItemTypeRepository 는 또 다른 인터페이스입니다.
public interface CustomItemTypeRepository {
void deleteCustomById(ItemType entity);
}
구현은 JPA뿐만 아니라 모든 종류의 저장소가 될 수 있습니다.
public class CustomItemTypeRepositoryImpl implements CustomItemTypeRepository {
@Autowired
private EntityManager entityManager;
@Override
public void deleteCustomById(ItemType itemType) {
entityManager.remove(itemType);
}
}
Impl 접미사가 있는지 확인하기만 하면 됩니다 . 그러나 다음 XML 구성을 사용하여 사용자 지정 접미사를 설정할 수 있습니다.
<repositories base-package="com.baeldung.repository" repository-impl-postfix="CustomImpl" />
또는 다음 어노테이션을 사용하여:
@EnableJpaRepositories(
basePackages = "com.baeldung.repository", repositoryImplementationPostfix = "CustomImpl")
4. 여러 조각을 사용하여 저장소 구성
몇 가지 릴리스 전까지는 단일 사용자 지정 구현을 사용하여 리포지토리 인터페이스만 확장할 수 있었습니다. 모든 관련 기능을 단일 개체로 가져와야 하는 제한 사항이었습니다.
말할 필요도 없이 복잡한 도메인 모델을 사용하는 대규모 프로젝트의 경우 이는 부풀려진 클래스로 이어집니다.
이제 Spring 5에서는 여러 조각 저장소로 JPA 저장소를 보강 할 수 있는 옵션이 있습니다. 다시 말하지만, 이러한 조각을 인터페이스-구현 쌍으로 가지고 있어야 한다는 요구 사항이 남아 있습니다.
이를 시연하기 위해 두 개의 프래그먼트를 생성해 보겠습니다.
public interface CustomItemTypeRepository {
void deleteCustom(ItemType entity);
void findThenDelete(Long id);
}
public interface CustomItemRepository {
Item findItemById(Long id);
void deleteCustom(Item entity);
void findThenDelete(Long id);
}
물론 구현을 작성해야 합니다. 그러나 이러한 사용자 정의 리포지토리(관련 기능 포함)를 자체 JPA 리포지토리에 연결하는 대신 단일 JPA 리포지토리의 기능을 확장할 수 있습니다.
public interface ItemTypeRepository
extends JpaRepository<ItemType, Long>, CustomItemTypeRepository, CustomItemRepository {
}
이제 하나의 단일 리포지토리에 연결된 모든 기능이 있습니다.
5. 모호함 다루기
우리는 여러 리포지토리에서 상속하기 때문에 충돌이 발생할 경우 어떤 구현이 사용될지 파악하는 데 어려움을 겪을 수 있습니다. 예를 들어, 이 예에서 두 프래그먼트 리포지토리에는 동일한 서명을 가진 findThenDelete() 메서드가 있습니다.
이 시나리오에서는 인터페이스 선언 순서를 사용하여 모호성을 해결합니다 . 결과적으로 우리의 경우 CustomItemTypeRepository 내부의 메서드 가 먼저 선언되었으므로 사용됩니다.
다음 테스트 사례를 사용하여 이를 테스트할 수 있습니다.
@Test
public void givenItemAndItemTypeWhenDeleteThenItemTypeDeleted() {
Optional<ItemType> itemType = composedRepository.findById(1L);
assertTrue(itemType.isPresent());
Item item = composedRepository.findItemById(2L);
assertNotNull(item);
composedRepository.findThenDelete(1L);
Optional<ItemType> sameItemType = composedRepository.findById(1L);
assertFalse(sameItemType.isPresent());
Item sameItem = composedRepository.findItemById(2L);
assertNotNull(sameItem);
}
6. 결론
이 기사에서는 Spring Data JPA 리포지토리를 사용할 수 있는 다양한 방법을 살펴보았습니다. 우리는 Spring이 많은 코드나 심지어 SQL 쿼리를 작성하지 않고도 도메인 개체에 대한 데이터베이스 작업을 간단하게 수행할 수 있도록 한다는 것을 확인했습니다.
이 지원은 구성 가능한 리포지토리를 사용하여 상당히 사용자 정의할 수 있습니다.
이 문서의 코드 스니펫은 여기 GitHub 에서 Maven 프로젝트 로 사용할 수 있습니다 .