본문 바로가기

개인적으로 공부한 것을 정리해 놓은 블로그입니다 틀린 것이 있으면 댓글 부탁 드립니다!


JPA/QueryDSL

QueryDSL 공부 3 - QueryDSL 활용1

반응형

 

1. where 절  검색 조건 쿼리 

 

where 절에 JPQL에서 제공하는 문법을 모두 제공한다 . (=  , >= ,<= ,>, < , like , between 등등 . .  )

 

  @Test
    void search(){
        Member findMember = qf
                .selectFrom(member)
                .where(
                // and 사용하지 않고 , 으로도 표현 가능
                        member.username.eq("member1")
                        ,member.age.eq(10)
                        ,member.username.startsWith("mem")
                        ,member.id.in(1L,2L,3L))
                .fetchOne();
        assertEquals(findMember.getUsername(),"member1");
        assertEquals(findMember.getAge(),10);
    }

 

where의 파라미터는 ... 문법 으로 여러개를 받기 때문에 위의 코드처럼 and로 조건을 사용하지 않고 쉼표 ,  로 구분해줘도 동작한다.

 

 

2. 결과 조회

 

 결과 조회를 하는 메서드는 아래와같다 

 

@Test
    void resultTest(){
        
        //.fetchOne() 단건조회
        Member member = qf
                .selectFrom(QMember.member)
                .where(
                        QMember.member.username.eq("member1"))
                .fetchOne();

        //.fetchFirst() limit(1).fetchOne과 같음;
        Member fetchFirst = qf
                .selectFrom(QMember.member)
                .where(
                        QMember.member.username.eq("member1"))
                .fetchFirst();

        //.fetchResults() 페이징 정보 포함
        QueryResults<Member> fetchResults = qf
                .selectFrom(QMember.member)
                .where(
                        QMember.member.username.eq("member1"))
                .fetchResults();
        long limit = fetchResults.getLimit();
        long offset = fetchResults.getOffset();
        List<Member> results = fetchResults.getResults();
        long total = fetchResults.getTotal();

        //fetchCount(); 카운트 쿼리만 나감 
        long fetchCount = qf
                .selectFrom(QMember.member)
                .where(
                        QMember.member.username.eq("member1"))
                .fetchCount();
        
    }

//.fetch() 리스트 조회
//.fetchFirst() limit(1).fetchOne과 같음;
//.fetchResults() 페이징 정보 포함 , total count 쿼리 추가로 실행
//.fetchCount() count쿼리 조회

 

 

fetchResult의 경우 페이징 쿼리가 복잡해 질때 컨텐츠를 가져오는 쿼리와 실제 total 카운트를 가져오는 쿼리를 성능 최적화를 위해 변경해야 할 수도 있다. 그럴 경우에는 fetchResult  를 사용 하는 것 보다는 query를 튜닝하여 직접 쿼리를  따로 날리는 것이 좋다 . 

 

 

3.정렬

 

 

 

    @Test
    void sort(){
        em.persist(new Member(null,100));
        em.persist(new Member("member5",100));
        em.persist(new Member("member6",100));

        List<Member> result= qf.selectFrom(member)
                .where(member.age.eq(100))
                .orderBy(member.username.asc().nullsLast(), member.age.desc())
                .fetch();

        Member member5 = result.get(0);
        Member member6 = result.get(1);
        Member memberNull = result.get(2);

        assertEquals(member5.getUsername(),"member5");
        assertEquals(member6.getUsername(),"member6");
        assertEquals(memberNull.getUsername(),null);
    }

 

orderBy() 메서드로 정렬조건을 추가해 줄 수 있다 ,  asc , desc , nullsLast(Null이면 마지막으로 정렬 ), nullFirst(Null이면 마지막으로 정렬) 등으로 정렬 해줄 수 있고 , where 절과 마찬 가지고 여러개의 조건을 , 으로 구분하여 넣어 줄 수 있다. 

 

 

4.페이징

 

 

 

@Test
    void paging1(){
        List<Member> result= qf.selectFrom(member)
                .where(member.username.like("member%"))
                .orderBy(member.username.asc())
                .offset(0)
                .limit(2)
                .fetch();

        assertEquals(result.size() , 2);
    }

    @Test
    void paging2(){
        QueryResults<Member> results = qf.selectFrom(member)
                .where(member.username.like("member%"))
                .orderBy(member.username.asc())
                .offset(0)
                .limit(2)
                .fetchResults();
        
        assertEquals(results.getResults().size() , 2);
        assertEquals(results.getTotal(), 4);

    }

 

paging 1의 경우 offset과 limit를 줘서 컨탠츠 갯수만 제한을 줘서 가져오는 것이고 

 

paging 2는 fetchResults로 받아 페이징 정보(totalCount , result,limit,offsett)를 포함해서 가져온다 . 

 

paging 2의 경우에는 총 갯수를 구하는 쿼리와 , 엔티티 리스트를 조회하는 쿼리 두방이 나가는데 

 

이전에 말했던 것처럼 해당 쿼리에 대해서 튜닝이 필요한 경우에는 fetchResults보다는 각각 쿼리를 튜닝하여 따로 날리는게 더 좋다 .

 

 

4.집합

 

 @Test
    void aggregation(){
        List<Tuple> result = qf
                .select(
                        member.count(),
                        member.age.sum(),
                        member.age.avg(),
                        member.age.max(),
                        member.age.min())
                .from(member)
                .fetch();
        //List내에 Tuple은 타입이 여러개일 때 사용한다 .
        //아래의 튜플은  queryDsl의 튜플이다
        Tuple tuple = result.get(0);
        Long count = tuple.get(member.count());
        assertEquals(count,4);
        assertEquals(tuple.get(member.age.sum()),46);
        assertEquals(tuple.get(member.age.avg()), (46/4f));

    }
   
@Test
void group(){
     //given
    List<Tuple> fetch = qf.select(team.name, member.age.avg())
                .from(member)
                .join(member.team, team)
                .groupBy(team.name)
                .fetch();

        Tuple teamA = fetch.get(0);
        Tuple teamB = fetch.get(1);

        assertEquals(teamA.get(team.name),"teamA");
        assertEquals(teamA.get(member.age.avg()),10.5f);
}

 

aggregation 테스트를 보면 함수를 활용하여 멤버의  합 , 평균 , 최대값,최소값을 조회하고 있다. 

이 경우 반환 타입이 Tuple로 나오는데 Tuple은 결과값이 여러가지 타입을 가졌을때 반환되는 타입이고 위의 Tuple은 queryDsl의 Tuple이다 . queryDsl 에서 tuple 객체에 담긴 값은 위의 tuple.get(member.count()) 같이 조회 때 사용한 필드명으로 빼서 쓸 수 있다 . 

 

gropBy도 사용할 수 있다 .   .groupBy()로 그룹할 조건을 지정해 주면되고 Having()도 사용 가능하다 . 

위의 경우 team이 같은 멤버를 그룹으로 하고 그 그룹의 나이의 평균값을 구하는 코드이다 

 

위 처럼 select절에서 엔티티가 아닌 값 타입으로 조회할 때는 위처럼 각각의 컬럼명을 지정해서 tuple로 받아서 쓰는 것보다는 

Dto를 만들어서 조회하는 것이 좋다 .  

반응형