JPA 에 Querydsl 추가(2)

JIWOO YUN·2024년 3월 26일
0

Querydsl

목록 보기
6/7

Spring Data JPA로 변경 진행

  • 이전까지 순수 JPA를 통해서 repo를 구성했던 것을 SpringData JPA 로 변경하자.

MemberRepository 추가

public interface MemberRepository extends JpaRepository<Member,Long> {

    List<Member> findByUsername(String username);
}

기본적으로 주어지는 JpaRepository에 있는 findAll(), findById() 의 경우 사용하는데 아무 지장 없지만 우리가 만들었던 querydsl 전용 기능인 search() 는 작성할 수없다.

  • 복잡한 기능을 쓰거나 사용자 커스텀을 사용하기 위해서는 사용자 정의 리포지토리를 이용해야한다.
    • 사용법은 따로 커스텀 인터페이스를 만들어서 extends 하는 방법이 있다.

이미지로 이해하는게 빠르기 때문에 이미지를 가져왔다.

  1. 사용자 커스텀 인터페이스를 작성해서 내가 추가할 기능이 뭔지 적어준다.

    1. 현재의 경우 search 함수를 추가할거기 때문에 인터페이스에 적어줌.
    public interface MemberRepositoryCustom {
    
        List<MemberTeamDto> search(MemberSearchCond cond);
    }
  2. 적어둔 인터페이스를 구현할 구현부를 따로 만들어준다.

public class MemberRepositoryImpl implements MemberRepositoryCustom {


    private final JPAQueryFactory queryFactory;

    public MemberRepositoryImpl(EntityManager em) {
        this.queryFactory = new JPAQueryFactory(em);
    }

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

    private BooleanExpression usernameEq(String username) {
        return hasText(username) ? member.username.eq(username) : null;
    }

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

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

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

    private BooleanExpression ageBetween(int ageLoe, int ageGoe) {
        return ageGoe(ageGoe).and(ageLoe(ageLoe));
    }
}
  1. memberRepository가 내가 만든 사용자 정의 인터페이스를 상속함으로써 사용이 가능해진다.
public interface MemberRepository extends JpaRepository<Member,Long>,MemberRepositoryCustom {

    List<Member> findByUsername(String username);
}

이렇게 하면 search 함수를 사용할수 있게된다.


Querydsl 페이징 연동하기

  • 스프링 데이터의 Page,Pageable 활용
  • querydsl 5.0.0 부터는 fetchResult() 와 fetchCount() 는 deperate 됬기 때문에 사용이 불가능
    • 데이터 내용과 전체 카운트를 별도로 조회하는 방법을 사용해야한다.
데이터 내용과 전체 카운트를 별도로 조회 코드
  • 전에 만든 사용자 정의 인터페이스에 searchPageComplex 를 추가해주고 구현부에 밑의 코드를 추가한다.
@Override
public Page<MemberTeamDto> searchPageComplex(MemberSearchCond cond, Pageable pageable) {


    List<MemberTeamDto> content = queryFactory
            .select(new QMemberTeamDto(
                    member.id,
                    member.username,
                    member.age,
                    team.id,
                    team.name
            ))
            .from(member)
            .leftJoin(member.team, team)
            .where(usernameEq(cond.getUsername()),
                    teamNameEq(cond.getTeamName()),
                    ageGoe(cond.getAgeGoe()),
                    ageLoe(cond.getAgeLoe()))
            .offset(pageable.getOffset())
            .limit(pageable.getPageSize())
            .fetch();


    Long total = queryFactory
            .select(member.count())
            .from(member)
            .leftJoin(member.team, team)
            .where(usernameEq(cond.getUsername()),
                    teamNameEq(cond.getTeamName()),
                    ageGoe(cond.getAgeGoe()),
                    ageLoe(cond.getAgeLoe()))
            .fetchOne();


    return new PageImpl<>(content,pageable,total);
}
  • total을 따로 세서 날리면 좋은 이유
    • total을 사용시에 필요없는 것들을 조회하지 않아서 최적화가 가능하다. -> 효율성 증가.

CountQuery 최적화

  • pageableExecutionUtils.getPage()를 통한 최적화

  • Count 쿼리가 생략 가능한 경우 생략해서처리

    • 페이지 시작이면서 컨텐츠 사이즈가 페이지 사이즈보다 작은 경우
    • 마지막 페이지 일 때 (마지막 페이지이면서 컨텐츠 사이즈가 페이지 사이즈 보다 작은 경우)
      • offset + 컨텐츠 사이즈를 더해서 전체 사이즈를 구한다.

CountQuery 추가 진행

    @Override
    public Page<MemberTeamDto> searchPageComplex(MemberSearchCond cond, Pageable pageable) {


        List<MemberTeamDto> content = queryFactory
                .select(new QMemberTeamDto(
                        member.id,
                        member.username,
                        member.age,
                        team.id,
                        team.name
                ))
                .from(member)
                .leftJoin(member.team, team)
                .where(usernameEq(cond.getUsername()),
                        teamNameEq(cond.getTeamName()),
                        ageGoe(cond.getAgeGoe()),
                        ageLoe(cond.getAgeLoe()))
                .offset(pageable.getOffset())
                .limit(pageable.getPageSize())
                .fetch();


//        Long total = queryFactory
//                .select(member.count())
//                .from(member)
//                .leftJoin(member.team, team)
//                .where(usernameEq(cond.getUsername()),
//                        teamNameEq(cond.getTeamName()),
//                        ageGoe(cond.getAgeGoe()),
//                        ageLoe(cond.getAgeLoe()))
//                .fetchOne();


        //countQuery 추가
        JPAQuery<Member> countQuery = queryFactory
                .select(member)
                .from(member)
                .leftJoin(member.team, team)
                .where(usernameEq(cond.getUsername()),
                        teamNameEq(cond.getTeamName()),
                        ageGoe(cond.getAgeGoe()),
                        ageLoe(cond.getAgeLoe()));


//        return new PageImpl<>(content,pageable,total);

        return PageableExecutionUtils.getPage(content,pageable,countQuery::fetchCount);
    }
  • 맨밑의 getPage부분에서 countQuery:: fetchCount가 이해가 안되서 찾아보니 내가 이해한 바로는 PageableExecutionUtils 이함수가 간접적으로 호출을 하다보니 querydsl과는 상관이 없고 JPAQuery 문서를 들어가서 확인해보니 @deperate 어노테이션이 붙어있지만 권장하지는 않아도 사용이 가능하기 때문에 사용할 수있는 걸로 이해했다.
    • "나중에 문서에서 사라질 수도 있다" 라고 알고 있기로 하자.
profile
열심히하자

0개의 댓글