
이전 포스팅에서 순수 JPA 와 Querydsl 을 사용해서 회원(Member) 리포지토리를 구현해보았다.
이번에는 스프링 데이터 JPA 와 Querydsl 을 사용해서 구현해보자.
( 참고로 리포지토리 구현을 위한 사전 작업은 이전 포스팅을 참고하자. )
1) MemberRepository 인터페이스 생성
스프링 데이터 JPA 를 사용하기 위해서는 인터페이스 상속을 위한 인터페이스가 필요하다.
따라서 MemberRepository 인터페이스를 생성하였고, 스프링 데이터 JPA 를 사용하기 위해 JpaRepository 를 상속받았다.
public interface MemberRepository extends JpaRepository<Member, Long> {
// 1) save 제공
// 2) findById 제공
// 3) findAll 제공
// 4) findBy.. 제공 ( 스프링 데이터 JPA는 메서드 이름으로 JPQL 쿼리를 생성하는 전략을 제공 )
List<Member> findByUsername(String username);
}
이전에 순수 JPA 와 Quesydsl 을 사용해서 리포지토리를 작성했을 때는, 등록(save), 조회(findByID) 를 위한 로직을 직접 작성해주었지만, 이제는 스프링 데이터 JPA를 사용하므로 그러한 기본적인 CRUD 는 공통 인터페이스 기능으로 해결 가능하다.
그런데 문제는 동적 쿼리에 있다. Querydsl 을 사용하려면 결국 구현 코드를 작성해야 하는데, 스프링 데이터 JPA 는 인터페이스로 동작한다. 따라서 구현 코드를 작성하는 것이 제한되는 것이다.
실무에서 스프링 데이터 JPA 를 사용하면서 구현 코드를 넣고자 할때는 사용자 정의 리포지토리 라는 방법을 사용할 수 있다. 사용자 정의 리포지토리 사용법은 다음과 같다.
사용자 정의 리포지토리 사용법
MemberRepositoryCustom )MemberRepositoryCustomImpl )MemberRepository 에서 MemberRepositoryCustom 상속 )위 사용법대로 하나씩 적용해보자.
2) 사용자 정의 인터페이스 작성 ( MemberRepositoryCustom )
사용자 정의 인터페이스( MemberRepositoryCustom )를 생성하자.
public interface MemberRepositoryCustom {
List<MemberTeamDto> searchByBuilder(MemberSearchCondition condition);
List<MemberTeamDto> searchByWhere(MemberSearchCondition condition);
}
3) 사용자 정의 인터페이스 구현 ( MemberRepositoryCustomImpl )
사용자 정의 인터페이스를 구현하는 클래스( MemberRepositoryCustomImpl )를 생성하자.
public class MemberRepositoryCustomImpl implements MemberRepositoryCustom {
private final JPQLQueryFactory queryFactory;
public MemberRepositoryCustomImpl(EntityManager em) {
this.queryFactory = new JPAQueryFactory(em);
}
@Override
public List<MemberTeamDto> searchByBuilder(MemberSearchCondition condition) {
BooleanBuilder builder = new BooleanBuilder();
if (StringUtils.hasText(condition.getUsername())) {
builder.and(member.username.eq(condition.getUsername()));
}
if (StringUtils.hasText(condition.getTeamName())) {
builder.and(team.name.eq(condition.getTeamName()));
}
if (condition.getAgeGoe() != null) {
builder.and(member.age.goe(condition.getAgeGoe()));
}
if (condition.getAgeLoe() != null) {
builder.and(member.age.loe(condition.getAgeLoe()));
}
return queryFactory
.select(new QMemberTeamDto(
member.id,
member.username,
member.age,
team.id,
team.name))
.from(member)
.leftJoin(member.team, team)
.where(builder)
.fetch();
}
@Override
public List<MemberTeamDto> searchByWhere(MemberSearchCondition condition) {
return queryFactory
.select(new QMemberTeamDto(
member.id,
member.username,
member.age,
team.id,
team.name
))
.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) {
return StringUtils.hasText(username) ? member.username.eq(username) : null;
}
private BooleanExpression teamNameEq(String teamName) {
return StringUtils.hasText(teamName) ? 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;
}
4) 스프링 데이터 리포지토리에서 사용자 정의 인터페이스 상속
MemberRepository 에서 MemberRepositoryCustom 를 상속받으면 된다.
public interface MemberRepository extends JpaRepository<Member, Long>, MemberRepositoryCustom {
...
}
이제 MemberRepository 를 통해서 MemberRepositoryCustom 에 정의된 searchByBuilder(), searchByWhere() 메서드를 사용할 수 있게 된다.
강의를 듣고 정리한 글입니다. 코드와 그림 등의 출처는 김영한 강사님께 있습니다.