MemberRepositoryCustom
package study.querydsl.repository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import study.querydsl.dto.MemberSearchCondition;
import study.querydsl.dto.MemberTeamDto;
import java.util.List;
public interface MemberRepositoryCustom {
List<MemberTeamDto> search(MemberSearchCondition condition);
Page<MemberTeamDto> searchPageSimple(MemberSearchCondition condition, Pageable pageable);
Page<MemberTeamDto> searchPageComplex(MemberSearchCondition condition, Pageable pageable);
}
- inferface 생성 후 구현체 메소드 작성
- 페이징을 할때는 Page라는 객체에 <반환 타입>을 매핑해주고 매개변수로 Pageable을 사용한다.
MemberRepositoryCustom
package study.querydsl.repository;
import com.querydsl.core.QueryResults;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
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;
public class MemberRepositoryImpl implements MemberRepositoryCustom {
private final JPAQueryFactory queryFactory;
public MemberRepositoryImpl(JPAQueryFactory queryFactory) {
this.queryFactory = queryFactory;
}
@Override
public List<MemberTeamDto> search(MemberSearchCondition condition) {
return queryFactory
.select(new QMemberTeamDto(
member.id.as("memberId"),
member.username,
member.age,
member.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();
}
@Override
public Page<MemberTeamDto> searchPageSimple(MemberSearchCondition condition, Pageable pageable) {
QueryResults<MemberTeamDto> queryResults = queryFactory
.select(new QMemberTeamDto(
member.id.as("memberId"),
member.username,
member.age,
member.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())
)
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetchResults();
List<MemberTeamDto> content = queryResults.getResults();
long total = queryResults.getTotal();
return new PageImpl<>(content, pageable, total);
}
@Override
public Page<MemberTeamDto> searchPageComplex(MemberSearchCondition condition, Pageable pageable) {
List<MemberTeamDto> content = getMemberTeamDtos(condition, pageable);
long total = getTotal(condition);
return new PageImpl<>(content, pageable, total);
}
private List<MemberTeamDto> getMemberTeamDtos(MemberSearchCondition condition, Pageable pageable) {
List<MemberTeamDto> content = queryFactory
.select(new QMemberTeamDto(
member.id.as("memberId"),
member.username,
member.age,
member.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())
)
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();
return content;
}
private long getTotal(MemberSearchCondition condition) {
long total = queryFactory
.selectFrom(member)
.leftJoin(member.team, team)
.where(
usernameEq(condition.getUsername()),
teamNameEq(condition.getTeamName()),
ageGoe(condition.getAgeGoe()),
ageLoe(condition.getAgeLoe())
)
.fetchCount();
return total;
}
private BooleanExpression usernameEq(String username) {
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;
}
}
- 구현 클래스 작성
searchPageSimple()
은 offset(), limit(), fetchResults()
로 마무리해준다.
offset()
은 페이지를 의미하고
limit()
는 몇개 씩 보여줄지 정할 수 있고
fetchResults()
는 list와 count를 동시에 해준다.
result
가 searchPageSimple() 호출한 return 결과 값이라면
result.getSize()
: totalCount
result.getContent()
: list
- return은
new PageImpl<>(content, pageable, total)
으로 하면 된다.
PageImpl
클래스가 Page
클래스에 상속 받아져 있기때문이다.
searchPageComplex()
은 list와 count 쿼리를 분리한 것이고 리팩토링을 하여 getMemberTeamDtos(), getTotal()
로 분리한 것이다.
MemberRepository
package study.querydsl.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import study.querydsl.entity.Member;
import java.util.List;
@Repository
public interface MemberRepository extends JpaRepository<Member, Long>, MemberRepositoryCustom {
List<Member> findByUsername(String username);
}
MemberRepository
는 JpaRepository
와 MemberRepositoryCustom
를 상속받으면 Spring Data JPA와 QueryDsl를 같이 쓸 수 있는 것이다.
- 그렇지만 이렇게 사용자 Repository를 만드는 것이 정답은 아니다. QueryDsl만 있는 Repository를 따로 만들어서 사용해도 무관하다.