[Spring / Jpa / querydsl ] 커스텀 리포지토리 구조

스프링 데이터 JPA와 querydsl

Custom Repository를 사용하기

Spring Data Jpa와 Querydsl을 활용한 custom repository(사용자 정의 리포지토리)를 사용하려면 아래와 같은 상속 구조로 구현해주면 된다.

위 사진보다 더 직관적으로 이해가 잘가는 사진이 있어서 가져왔다.

사진 출처: https://wildeveloperetrain.tistory.com/322

 

 

 


MemberRepositoryCustom (인터페이스)

사용자 정의 메서드를 정의한다.

package study.querydsl.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import study.querydsl.entity.Member;

import java.util.List;

public interface MemberRepository extends JpaRepository<Member, Long>, MemberRepositoryCustom {
    List<Member> findAllByUsername(String username);

}

 

 


MemberRepository (인터페이스)

JpaRepository와 MemberRepositoryCustom을 상속(extend)받도록 한다.

package study.querydsl.repository;

import com.querydsl.jpa.impl.JPAQueryFactory;
import org.springframework.data.jpa.repository.JpaRepository;
import study.querydsl.dto.MemberSearchCondition;
import study.querydsl.dto.MemberTeamDto;
import study.querydsl.entity.Member;

import java.util.List;

public interface MemberRepositoryCustom  {

    List<MemberTeamDto> search(MemberSearchCondition condition);
}

 


MemberRepositoryImpl

이 구현체는 이름을 반드시 [ 리포지토리 + Impl ]로 지어주어야한다.

이 구현체는 MemberRepositoryCustom을 구현(implement) 한다.

네이밍 규칙을 지켜야하는 이유는, Spring Data Jpa에 의해서 MemberRepository가 스프링 빈으로 등록될때 이 리포지토리의 구현체로서 “Impl”을 postfix로 가지는 구현체가 있는지 자동으로 스캔한다.

즉, 이 상황에서는 “MemberRepositoryImpl”이라는 이름의 클래스가 있는지 찾게 된다.

(MemberRepositoryImpl이 반드시 존재해야하는 것은 아니다.)

그리고 MemberRepository를 빈으로 등록하며, MemberRepositoryImpl을 의존성 주입한다.

package study.querydsl.repository;

import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import org.springframework.util.StringUtils;
import study.querydsl.dto.MemberSearchCondition;
import study.querydsl.dto.MemberTeamDto;
import study.querydsl.dto.QMemberTeamDto;

import java.util.List;

import static study.querydsl.entity.QMember.member;
import static study.querydsl.entity.QTeam.team;

// 여기는 이름을 꼭 맞춰주어야함 ~~Repository + Impl
public class MemberRepositoryImpl implements MemberRepositoryCustom {
    private final JPAQueryFactory queryFactory;

    // JPAQueryFactory가 스프링 빈으로 등록돼 있다면 그걸 사용. 아니라면 이렇게 사용해도 상관없다.
    public MemberRepositoryImpl(EntityManager em) {
        queryFactory = new JPAQueryFactory(em);
    }

    @Override
    public List<MemberTeamDto> search(MemberSearchCondition condition) {
        return queryFactory.select(new QMemberTeamDto(
                        member.id.as("memberId"),
                        member.username,
                        member.age,
                        team.id.as("teamId"),
                        team.name.as("teamName")
                ))
                .from(member)
                .leftJoin(member.team, team)
                .where(
                        usernameEq(condition.getUsername()),
                        teamNameEq(condition.getTeamName()),
                        ageGoe(condition.getAgeGoe()),
                        ageLoe(condition.getAgeLoe())
                )
                .fetch();
    }

    private BooleanExpression usernameEq(String username) { // BooleanExpression은 조합이 가능하다.
        return StringUtils.hasText(username) ? member.username.eq(username) : null;
    }

    private BooleanExpression teamNameEq(String teamName) {
        return StringUtils.hasText(teamName) ? member.team.name.eq(teamName) : null;
    }

    private BooleanExpression ageGoe(Integer ageGoe) {
        return ageGoe != null ? member.age.goe(ageGoe) : null;
    }

    private BooleanExpression ageLoe(Integer ageLoe) {
        return ageLoe != null ? member.age.loe(ageLoe) : null;
    }
}

즉, 위 구조를 다시 정리하자면

MemberRepository는 애플리케이션(서비스) 레이어에서 사용하는 인터페이스이고,

이에 대한 구현은 JpaRepository의 구현체와 MemberRepositoryImpl이 하게된다.

하지만 이와 다른 구조로도 커스텀 리포지토리를 구성할 수 있다.

예를 들면

  • MemberRepository (모든 메서드가 정의되어있음. impl에 있는 것 + MemberJpaRepository에 있는것)
  • MemberJpaRepository (JpaRepository를 extends하며 Spring Data Jpa의 메서드들이 들어감. ex. findByName)
  • MemberRepositoryImpl (MemberRepository를 구현함. 내부에서 MemberJpaRepository를 빈으로 주입받아, 메서드를 구현)

 


Reference

인프런 강의 <실전! Querydsl> (김영한)

https://wildeveloperetrain.tistory.com/322

 

repository, repositoryCustom, repositoryImpl 구조를 사용하자

jpa 공식 같은 repository, repositoryCustom, repositoryImpl 구조 해당 포스팅에서는 하나의 Repository를 통해 JpaRepository의 기능과 Querydsl의 기능을 사용하는 CustomRepository를 모두 사용할 수 있는, 일종의 공식

wildeveloperetrain.tistory.com

 

https://yeon-kr.tistory.com/229

 

JPA Repository 기본 postfix로 인한 순환참조 해결

1) 서론 요즈음 스프링 프레임워크를 사용한다면 Spring Data JPA는 기본적으로 사용하게 되는데요. ORM 프레임워크는 RDB의 테이블을 객체로서 나타내고 맵핑할 수 있게 합니다. 애플리케이션에서 테

yeon-kr.tistory.com

 

 

 

 

 

 

 

myoskin