이전 포스팅에서는 Query DSL을 사용하여 데이터를 조회하는 간단한 예시를 만들었었습니다. 이번 포스팅에서는 조회한 결과물을 정렬하고 페이징 처리하는 방법에 대해 알아볼게요😄
개요
이전 포스팅에서 사용했던 엔티티들을 재사용 할 예정입니다. 혹여 엔티티 클래스의 코드가 궁금하신 분들은 엔티티 설계 포스팅을 참고해주세요😄
📌 저는 BeforeEach 어노테이션을 활용해 가데이터가 들어간 상황에서 테스트를 진행한 것입니다.
@BeforeEach
public void before() {
queryFactory = new JPAQueryFactory(em);
Team teamA = new Team("teamA");
Team teamB = new Team("teamB");
em.persist(teamA);
em.persist(teamB);
Member member1 = new Member("member1", 10, teamA);
Member member2 = new Member("member2", 20, teamA);
Member member3 = new Member("member3", 30, teamB);
Member member4 = new Member("member4", 40, teamB);
em.persist(member1);
em.persist(member2);
em.persist(member3);
em.persist(member4);
}
MEMBER_ID | NAME | AGE | TEAM_NAME |
1 | member1 | 10 | teamA |
2 | member2 | 20 | teamA |
3 | member3 | 30 | teamB |
4 | member4 | 40 | teamB |
정렬(Sort)📒
정렬하는 방법은 크게 2가지로 나뉩니다. 일반 정렬과 null 데이터 순서 정렬인데, 흔히 아는 desc(), asc()로 내림차순, 오름차순으로 정렬할 수 있으며, nullsLast(), nullsFirst() 메서드를 이용하여 null 데이터의 순서도 부여할 수 있습니다.
간단한 예제를 볼까요?
@Test
public void sort() {
em.persist(new Member(null, 100));
em.persist(new Member("member5", 100));
em.persist(new Member("member6", 100));
List<Member> result = queryFactory
.selectFrom(member)
.where(member.age.eq(100))
.orderBy(member.age.desc(), member.username.asc().nullsLast())
.fetch();
Member member5 = result.get(0);
Member member6 = result.get(1);
Member memberNull = result.get(2);
assertThat(member5.getUsername()).isEqualTo("member5");
assertThat(member6.getUsername()).isEqualTo("member6");
assertThat(memberNull.getUsername()).isNull();
}
테스트 코드를 보면 첫 번째로 가데이터 회원 3명을 넣었습니다. 그리고 Query DSL을 사용하여 나이가 100살인 회원들 중에 나이 필드를 이용하여 내림차순으로 하고, 나이가 같다면 이름으로 오름차순으로 정렬하였습니다.
그리고 nullsLast()를 이용하여 이름이 없는 회원을 맨 마지막에 출력하게 하였습니다.
예상 값을 member 엔티티로 반환 받고 assertThat으로 확인한 결과 조건에 맞게 데이터들이 정렬되어 나오는 것을 확인할 수 있었습니다😀
테스트는 성공하였고, DB에 전달 된 SQL을 보면 원하는 조건들은 모두 들어간 걸 확인할 수 있었습니다.
페이징(Paging)📗
@Test
public void paging() {
List<Member> result = queryFactory
.selectFrom(member)
.orderBy(member.username.desc())
.offset(1)
.limit(2)
.fetch();
assertThat(result.size()).isEqualTo(2);
}
페이징 처리 또한 간단하게 구현이 가능하다. offset을 통해 시작 index를 알리고, limit을 통해 가져올 결과물 수를 제한하면 된다. 사실 실무에서는 이렇게 데이터만 가져오는 페이징은 잘 사용하지 않고, 데이터의 총개수를 찾아서 이전/다음 페이지 유무, 총 페이지 수 등을 구해야 한다.
Query DSL 초기 버전에서는 위에서 사용한 fetch() 대신 fetchResults()를 이용하여 카운트 쿼리를 동시에 날려서 데이터의 총 갯수(원하는 페이지 X, where 조건 문에 합당하는 모든 데이터 수)를 가져올 수 있었다.
하지만 Query DSL 5.0 버전에서 fetchResults()의 사용을 추천하지 않는 다는 내용이 추가되었다. 그 이유는 count 쿼리가 모든 dialect에서 또는 다중 그룹 쿼리에서 완벽하게 지원되지 않기 때문이다.
그래서 대부분 카운트 쿼리가 필요할 때는 따로 쿼리를 작성하곤 한다😀
@Test
public void paging() {
int totalSize = queryFactory
.selectFrom(member)
.fetch().size();
assertThat(totalSize)).isEqualTo(2);
}
집합(Aggregation)📔
@Test
public void aggregation() {
List<Tuple> result = queryFactory
.select(member.count(),
member.age.sum(),
member.age.avg(),
member.age.max(),
member.age.min()
)
.from(member)
.fetch();
Tuple tuple = result.get(0);
assertThat(tuple.get(member.count())).isEqualTo(4);
assertThat(tuple.get(member.age.sum())).isEqualTo(100);
assertThat(tuple.get(member.age.max())).isEqualTo(40);
assertThat(tuple.get(member.age.min())).isEqualTo(10);
}
집합 함수 또한 JPQL이 제공하는 모든 집합 함수를 제공한다. 위 코드와 같이 총 갯수를 구하는 count(), 합계 sum(), 평균 avg(), 최대값 max(), 최소값 min() 등 다양하게 지원한다.
GroupBy 사용📘
@Test
public void group() {
List<Tuple> result = queryFactory
.select(team.name, member.age.avg())
.from(member)
.join(member.team, team)
.groupBy(team.name)
.fetch();
Tuple teamA = result.get(0);
Tuple teamB = result.get(1);
assertThat(teamA.get(team.name)).isEqualTo("teamA");
assertThat(teamA.get(member.age.avg())).isEqualTo(15);
assertThat(teamB.get(team.name)).isEqualTo("teamB");
assertThat(teamB.get(member.age.avg())).isEqualTo(35);
}
select 절을 이용하여 해당 팀의 이름과 팀 안에 소속된 멤버들의 평균 나이를 구하고, groupBy를 사용하여 teamA와 teamB로 결과값을 받았습니다.
DB에 전달된 쿼리문을 살펴보면 원하는 요구사항에 맞게 조인과 group by가 조건문으로 들어가는 것을 확인할 수 있습니다.
📌그룹화된 결과를 제한하려면 having절을 사용해야 합니다.
👨👩👦👦 오픈채팅방 운영
취업을 준비하는 예비 개발자분들을 위한 질문&답변할 수 있는 공간을 만들었습니다. 취업과 이직을 하기 위해서 어떤 걸 중점적으로 준비해야 하는지부터 포트폴리오&이력서 작성법 등 다양한 질문들을 받고 답변을 드립니다. 참여하셔서 다양한 정보 얻고 가시면 좋을 것 같네요😁
참여코드 : 456456
https://open.kakao.com/o/gVHZP8dg
비전공 개발자 취업 준비방(질문&답변)
#비전공 #개발자 #취업 #멘토링 #부트캠프 #국비지원 #백엔드 #프론트엔드 #중소기업 #중견기업 #자바 #Java #sql
open.kakao.com
👨💻 전자책 출간
아울러 제가 🌟비전공자에서 2년만에 보안 전문 중견기업으로 이직 한 방법들을 정리한 전자책을 출간 하게 되었습니다. 어떤 걸 공부해야 하는지, 이직을 위해서 무엇을 준비해야 하는지, 제가 받았던 기술 면접 리스트 등 다양한 목차로 구성되어 있습니다. 또한, 구매 시 1:1 채팅을 이용하여 포트폴리오 첨삭을 도와드리고 있습니다. 🐕전자책으로 얻은 모든 수익은 유기견 센터 '팅*벨 입양센터'에 후원될 예정입니다. 관심 있으신 분들은 아래 링크를 참고해주세요😁
비전공개발자 2년만에 중견기업 들어간 방법 | 14000원부터 시작 가능한 총 평점 0점의 전자책, 취
0개 총 작업 개수 완료한 총 평점 0점인 Binco의 전자책, 취업·이직 전자책 서비스를 0개의 리뷰와 함께 확인해 보세요. 전자책, 취업·이직 전자책 제공 등 14000원부터 시작 가능한 서비스
kmong.com
마치며
지금까지 Query DSL을 사용하여 정렬하는 방법, 페이징 처리, 집합 관련 로직들을 간단한 예시를 통해 알아보았습니다. 다음 포스팅은 Query DSL을 이용하여 조인하는 방법에 대해 알아볼게요😄
QueryDSL Series
Reference
'JPA' 카테고리의 다른 글
QueryDSL Projection 결과 반환하는 방법 (0) | 2022.11.14 |
---|---|
QueryDSL 조인 사용 방법 (0) | 2022.11.01 |
QueryDSL 사용하는 조회 예제 (0) | 2022.10.28 |
QueryDSL 적용 및 예제 시리즈 (0) | 2022.10.27 |
Spring Data JPA Bulk Update & Entity Graph (0) | 2022.10.14 |