공부함
SpringDataJpa 사용자 정의 Repository 적용 본문
사용자 정의 Repository가 필요한 이유
public interface ReviewRepository extends JpaRepository<Review, Long>{
...
}
Spring이 JpaRepository를 상속하는 인터페이스의 구현체를 자동으로 생성해준다. Service에서는 자동 생성된 구현체를 주입받아 사용한다.
기본으로 제공하는 메서드들을 사용할 수 있다. 더해서 명명규칙에 따라 메서드를 작명하면 알아서 쿼리를 짜준다. 개발자는 메서드를 가져다 쓰기만 하면 되는 편리한 기능이다.
https://zara49.tistory.com/130
메서드 명명규칙에 대해서는 위 글을 참고하자.
하지만 명명규칙 만으로는 해결할 수 없고 쿼리를 직접 작성해야 할 때가 있기 마련이다.
https://velog.io/@codren/Query-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98
public interface ReviewRepository extends JpaRepository<Review, Long>, QueryDslReviewRepository {
...
@Query("select distinct function('date_format', r.createdAt, '%d') from Review r where r.member.id = :memberId and function('date_format', r.createdAt, '%m') = :month")
List<Integer> findAllByMonth(@Param("memberId") Long memberId, @Param("month") int month);
...
}
위와 같이 @Query 애노테이션을 사용할 수 있다. JPQL을 통해 쿼리를 작성할 수 있다. nativeQuery=true로 지정하면 JPQL이 아닌 SQL로 작성할 수 있다. (@Query에 관해서는 위 글을 참고하자.)
하지만 @Query로 직접 JPQL 쿼리를 작성하더라도 동적 쿼리 작성은 어렵다. 따라서 Querydsl을 사용하는 것이 좋다.
Querydsl 뿐만이 아니라 JPA 직접 사용, 스프링 JDBC Template 등이 필요할 때가 있다.
이럴 때 사용자 정의 Repository를 사용한다.
사용자 정의 Repository 사용 방법
public interface ReviewRepository extends JpaRepository<Review, Long>{
...
}
사용자 정의 리포지토리를 추가하려면 어떻게 해야 할까?
단순하게 CustomReviewRepository 클래스를 생성하고 사용하는 것을 생각할 수 있다. 실제로 내가 Querydsl을 처음 적용할 때 이렇게 하려고 했다.
public class ReviewService {
private final ReviewRepository reviewRepository;
private final CustomReviewRepository customReviewRepository;
...
}
JpaRepository의 메서드를 호출할 때는 reviewRepository를 사용하고, 커스텀한 메서드를 호출할 때는 customReviewRepository를 사용해야 한다. 불편하고 코드가 지저분해진다.
public class CustomReviewRepository implements ReviewRepository{
// 모든 메서드를 직접 구현해야 함!
}
그렇다면 CustomReviewRepository가 ReviewRepository를 구현하게 하고 커스텀 메서드를 CustomReviewRepository에 구현하면 되는 것 아닐까? 이렇게 하면 CustomReviewRepository로 ReviewRepository의 메서드도 호출할 수 있기 때문이다. 하지만 위에서 설명했듯이, Spring에서 JpaRepository의 구현체를 자동으로 생성해준다. 개발자가 직접 JpaRepository의 구현체를 구현하면 JpaRepository의 메서드를 전부 직접 구현해야 한다. 즉 Spring Data Jpa를 사용하는 의미가 없다.
그럼 도대체 어떻게 사용해야 할까?
public interface QueryDslProblemRepository {
PageImpl<Problem> findAll(
Pageable pageable, DataStructureConstant dataStructure, AlgorithmConstant algorithm, SortConstant orderBy);
}
public class QueryDslProblemRepositoryImpl implements QueryDslProblemRepository {
private final JPAQueryFactory jpaQueryFactory;
public PageImpl<Problem> findAll(
Pageable pageable, DataStructureConstant dataStructure, AlgorithmConstant algorithm, SortConstant orderBy) {
List<Problem> results = getResults(pageable, dataStructure, algorithm, orderBy);
long size = getSize(dataStructure, algorithm);
return new PageImpl<>(results, pageable, size);
}
...
}
@Repository
public interface ProblemRepository extends JpaRepository<Problem, Long>, QueryDslProblemRepository {
Optional<Problem> findByLink(String link);
}
이런 식으로 사용하면 된다. ProblemRepository가 JpaRepository와 QueryDslProblemRepository를 상속하게 한다.
QueryDslProblemRepository에는 커스텀 메서드들을 선언한다. 이에 대한 구현은 QueryDslProblemRepositoryImpl에서 한다. 이 때, 명명규칙을 지켜야 한다. 커스텀 repository를 구현하는 구현체의 이름은 커스텀repository+Impl의 형태여야 한다. 이렇게 규칙을 지켜 커스텀 repository를 만들면 ProblemRepository를 통해 커스텀 Repository 구현체에서 구현한 메서드들을 호출할 수 있다. 또한 Spring이 알아서 ProblemRepository의 구현체를 생성해주므로 JpaRepository의 메서드를 일일히 구현할 필요도 없다. 커스텀한 메서드와 JpaRepository의 메서드들을 하나의 객체로 전부 접근 가능해 코드가 간결해지고 관리가 쉬워진다.
'스프링' 카테고리의 다른 글
ApplicationContext (0) | 2024.08.28 |
---|---|
서블릿과 MVC 패턴과 프론트 컨트롤러 패턴 (1) | 2024.08.27 |
카카오 로그인 (0) | 2024.08.02 |
@RestControllerAdvice를 활용한 스프링 예외 처리 (0) | 2023.12.19 |
Pageable (0) | 2023.12.08 |