스프링부트

Spring Data MongoDB - Custom Repository 사용하기

알쓸개잡 2024. 3. 13.

Spring Data는 코드 작업 없이 쿼리 메서드를 자동으로 생성할 수 있는 프레임워크를 제공한다. 그러나 이러한 자동 생성 메서드 이외에도 정밀한 DB 작업을 수행하기 위해서 때로는 사용자 정의 메서드를 구현해야 하는 경우도 있다. (ex. aggregation과 같은..)

이러한 경우 inteface로 선언된 Repository 클래스 하나로 별도로 구현한 Custom Repository에 포함된 기능들과 더불어 자동 생성된 메서드를 사용할 수 있다.

 

먼저 Spring Data MongoDB를 사용하기 위한 연결 설정에 대한 내용은 아래 포스팅을 참고하기 바란다.

2024.01.01 - [스프링부트] - spring data MongoDB - MongoTemplate 설정하기

2023.12.29 - [스프링부트] - spring boot data MongoDB - 연결 설정 하기

 

 

Custom Repository 기본 골격

public interface MyCustomRepository {
	customMethod1(...);
	customMethod2(...);
	...
}

// 중요: 클래스명은 구현대상 인터페이스 클래스명 + Impl 접미사를 붙인 이름이어야 한다.
// Impl 접미사 대신 다른 접미사를 붙이도록 설정할 수 있다.
// Repository 인프라는 Repository를 찾은 패키지 아래에서 클래스를 검색하여 사용자 정의 클래스를
// 자동으로 감지하려고 시도하는데 이때 기본적으로 Impl 접미사가 붙은 클래스를 찾게 된다.
// 검색된 클래스는 자동으로 빈으로 등록된다.
@RequiredArgsConstructor
public class MyCustomRepositoryImpl implements MyCustomRepository {
	private final MongoTemplate mongoTemplate;
	
	@Override
	customMethod1(...) {
		mongoTemplate.xxxx();
		...
	}
	
	@Override
	customMethod2(...) {
		mongoTemplate.xxxx();
		...
	}
}

// Service 클래스에서 사용할 Repository
public interface MyRepository extends MongoRepository<Document, UUID>, MyCustomRepository {
	findByXXX(...)
}
MyCustomRepositoryImpl 클래스 상단에 주석에 언급했듯이 Custom Repository 구현체 클래스의 이름은 Custom Repository의 클래스명 + 'Impl' 접미사를 붙여야 한다. 'Impl'은 Custom Repository 인터페이스의 구현체 클래스를 찾는 기본 접미사이기 때문이다.

 

MyRepository 빈은 자동으로 구현체가 생성된 findByXXX() 메서드와 customMethod1(), customMethod2() 메서드를 모두 사용할 수 있다.

Repository를 사용하는 클래스에서는 MyRepository 인스턴스만 주입받아서 사용한다.

@Service
@RequiredArgsConstructor
public class Service {
	private final MyRepository myRepository;
	...
}

 

 

구현체 클래스명 접미사 변경

사실 기본 접미사 default 값은 EnableMongoRepositories 어노테이션 클래스를 확인해 보면 알 수 있다.

다음은 EnableMongoRepositories 어노테이션 클래스 정의 코드다.

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({MongoRepositoriesRegistrar.class})
public @interface EnableMongoRepositories {
    String[] value() default {};

    String[] basePackages() default {};

    Class<?>[] basePackageClasses() default {};

    ComponentScan.Filter[] includeFilters() default {};

    ComponentScan.Filter[] excludeFilters() default {};

    String repositoryImplementationPostfix() default "Impl";

    String namedQueriesLocation() default "";

    QueryLookupStrategy.Key queryLookupStrategy() default Key.CREATE_IF_NOT_FOUND;

    Class<?> repositoryFactoryBeanClass() default MongoRepositoryFactoryBean.class;

    Class<?> repositoryBaseClass() default DefaultRepositoryBaseClass.class;

    String mongoTemplateRef() default "mongoTemplate";

    boolean createIndexesForQueryMethods() default false;

    boolean considerNestedRepositories() default false;
}

String repositoryImplementationPostfix() default "Impl"; 로 Custom Repository 클래스 이름에 붙는 접미사는 Impl 임을 확인할 수 있다.

 

따라서 Custom Repository 인터페이스의 구현체 클래스를 찾는 기본 접미사를 변경하려면 Configuration 클래스에 다음과 같은 어노테이션으로 변경할 수 있다.

@EnableMongoRepositories(repositoryImplementationPostfix = "MyPostfix")
@Configuration
class Configuration {
	...	
}

위와 같이 repositoryImplementationPostfix="MyPostfix"로 지정한 경우 Custom Repository 인터페이스의 구현체 클래스 이름의 마지막 접미사에는 MyPostfix가 붙어야 한다. 위 샘플의 경우에는 다음과 같이 구현체 클래스명이 변경 되어야 한다.

MyCustomRepositoryImpl -> MyCustomRepositoryMyPostfix

 

 

우선순위

Custom Repository는 기본으로 제공되는 메서드보다 우선순위가 높다.

Custom Repository에서 제공하는 메서드와 기본으로 제공되는 메서드 시그니처가 모두 동일한 경우 Custom Repository에서 제공하는 메서드가 우선된다.

interface CustomizedSave<T> {
  <S extends T> S save(S entity);
}

@Slf4j
class CustomizedSaveImpl<T> implements CustomizedSave<T> {

  public <S extends T> S save(S entity) {
    log.info("called customized save method");
    // Your custom implementation
  }
}

CustomizedSave<T> 클래스는 기본으로 제공되는 save() 메서드와 동일한 시그니처를 갖고 있다.

MyRepository가 다음과 같이 CustomizedSave<T>를 추가로 확장한다면

// Service 클래스에서 사용할 Repository
public interface MyRepository extends MongoRepository<Document, UUID>, 
	MyCustomRepository, CustomizedSave<Document> {
	findByXXX(...)
}

MyRepository 인스턴스의 save() 메서드를 호출했을 때 기본으로 제공되는 save() 메서드가 아닌 CustomizedSave<Document> 인스턴스의 save() 메서드가 호출되어 'called customized save method' 로그가 출력될 것이다.

우선순위를 통해서 기본으로 제공되는 메서드와 Custom Repository에서 제공하는 메서드 시그니처가 모두 동일한 경우 어떤 메서드가 실행될지에 대한 모호성을 해결할 수 있다.

 

이상 끝.

 


아래 링크에 참고용 샘플코드가 있다.

샘플코드 gitlab 링크


참고링크

https://docs.spring.io/spring-data/jpa/reference/repositories/custom-implementations.html

댓글

💲 추천 글