스프링 데이터 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> (김영한)
'개념 공부 > Spring & ORM' 카테고리의 다른 글
[ Java / querydsl ] Queryds에서의 페이지네이션 (1) | 2025.03.29 |
---|---|
[Java / querydsl] querydsl로 동적쿼리를 처리하기 (3) | 2025.03.18 |
[Java / querydsl] 여러 Projection 방법 (0) | 2025.03.16 |
[Java / Spring] @Transational과 ChainedTransactionManager (0) | 2025.03.12 |
[JPA 개념] 연관 관계 매핑 (2) | 2024.06.06 |