BackEnd/Querydsl

03. Querydsl 기본 문법

hanseom 2021. 7. 17. 15:36
반응형

1. Querydsl 기본 문법


package com.spring.querydsl;

import com.querydsl.jpa.impl.JPAQueryFactory;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
import com.spring.querydsl.entity.Member;
import com.spring.querydsl.entity.QMember;
import com.spring.querydsl.entity.Team;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import static org.assertj.core.api.Assertions.*;
import static com.spring.querydsl.entity.QMember.*;


@SpringBootTest
@Transactional
public class QuerydslBasicTest {
    @PersistenceContext
    EntityManager em;

    JPAQueryFactory queryFactory;

    @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);
    }

    @Test
    public void startQuerydsl() {
        // EntityManager JPAQueryFactory 생성
        // JPAQueryFactory queryFactory = new JPAQueryFactory(em); > 필드 선언으로 변경


         // 기본 인스턴스 static import
        // QMember m = new QMember("m");
        // select m from Member m where m.username = :username;
        Member findMember = queryFactory
                     .select(member)
                     .from(member)
                     .where(member.username.eq("member1")) // Parameter Binding
                     .fetchOne();

        assertThat(findMember.getUsername()).isEqualTo("member1");
    }
}


  > Querydsl은 JPQL 빌더

  > JPQL: 문자(실행 시점 오류), Querydsl: 코드(컴파일 시점 오류)

  > JPQL: 파라미터 바인딩 직접, Querydsl: 파라미터 바인딩 자동 처리

  > JPAQueryFactory를 필드로 제공하면 동시성 문제는 JPAQueryFactory를 생성할 때 제공하는 EntityManager(em)에 달려있다. 스프링 프레임워크는 여러 쓰레드에서 동시에 같은 EntityManager에 접근해도, 트랜잭션 마다 별도의 영속성 컨텍스트를 제공하기 때문에 동시성 문제가 없다.

 

Q클래스 인스턴스 사용 방법

1) QMember qMember = new QMember("m"); //별칭 직접 지정

2) QMember qMember = QMember.member; //기본 인스턴스 사용

 

기본 인스턴스를 static import와 함께 사용

  > 같은 테이블을 조인해야 하는 경우가 아니면 기본 인스턴스 사용하기.

 

참고) 다음 설정을 추가하면 실행되는 JPQL을 볼 수 있다.

  > spring.jpa.properties.hibernate.use_sql_comments: true

 

2. Querydsl 조회


package com.spring.querydsl;

import com.querydsl.core.Tuple;
import com.querydsl.jpa.impl.JPAQueryFactory;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
import com.spring.querydsl.entity.Member;
import com.spring.querydsl.entity.QMember;
import com.spring.querydsl.entity.Team;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.List;

import static org.assertj.core.api.Assertions.*;
import static com.spring.querydsl.entity.QMember.*;


@SpringBootTest
@Transactional
public class QuerydslBasicTest {
    @PersistenceContext
    EntityManager em;

    JPAQueryFactory queryFactory;

    @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("member1", 20, teamA);
        Member member3 = new Member("member1", 20, teamB);
        Member member4 = new Member("member1", 40, teamB);
        em.persist(member1);
        em.persist(member2);
        em.persist(member3);
        em.persist(member4);
    }

    @Test
    public void search() {
        // com.querydsl.core.Tuple : 프로젝션(select 대상 지정) 대상이 둘 이상일 때 사용
        List<Tuple> result = queryFactory
        // select, from selectFrom으로 합칠 수 있다.
        // .selectFrom(member)
        .select(member.username,
        // 집합 함수
                 member.count(),
                 member.age.sum(),
                 member.age.avg(),
                 member.age.max(),
                 member.age.min())
        .from(member)
        // 검색 조건은 .and(), .or()를 메서드 체인으로 연결할 수 있다.
        // .where(member.username.eq("member1").and(member.age.gt(10)))
        // -> AND 조건을 파라미터로 처리하면 null 값은 무시한다.
        .where(member.username.eq("member1"), member.age.gt(10))
        /*
        member.username.eq("member1") // username = 'member1'
        member.username.ne("member1") //username != 'member1'
        member.username.eq("member1").not() // username != 'member1'
        member.username.isNotNull() //이름이 is not null
        member.age.in(10, 20) // age in (10,20)
        member.age.notIn(10, 20) // age not in (10, 20)
        member.age.between(10,30) //between 10, 30
        member.age.goe(30) // age >= 30
        member.age.gt(30) // age > 30
        member.age.loe(30) // age <= 30
        member.age.lt(30) // age < 30
        member.username.like("member%") //like 검색
        member.username.contains("member") // like ‘%member%’ 검색
        member.username.startsWith("member") //like ‘member%’ 검색
        */
        // Group by
        .groupBy(member.username, member.age)
        .having(member.age.gt(10))

        // Sort
    
    // 1.
회원 나이 내림차순(desc), 2. 회원 이름 올림차순(asc)
        // * 2에서 회원 이름이 없으면 마지막에 출력(nulls last)
        // nullsLast() , nullsFirst() : null 데이터 순서 부여
        .orderBy(member.age.desc(), member.username.asc().nullsLast())
        // Paging
        // .offset(1) //0부터 시작(zero index)
        // .limit(2) //최대 2건 조회
        .fetch();

        for (Tuple tuple : result) {
           System.out.println("t=" + tuple);
        }
    }
}


3. Querydsl 결과 조회

  1) fetch() : 리스트 조회, 데이터 없으면 빈 리스트 반환

  2) fetchOne() : 단 건 조회

    > 결과가 없으면 : null

    > 결과가 둘 이상이면 : com.querydsl.core.NonUniqueResultException

  3) fetchFirst() : limit(1).fetchOne()

  4) fetchResults() : 페이징 정보 포함, total count 쿼리 추가 실행

    > count 쿼리에 조인이 필요없는 성능 최적화가 필요하다면, count 전용 쿼리를 별도 분리

  5) fetchCount() : count 쿼리로 변경해서 count 수 조회

 

 

반응형
댓글수0