[QueryDSL] 기본 문법

윤경·2021년 12월 3일
0

QueryDSL

목록 보기
3/11
post-thumbnail

[1] 시작 - JPQL vs Querydsl

JPQL 코드

    @Test
    public void startJPQL() {
        // member1 찾기
        String qlString = "select m from Member m " +
                "where m.username = :username";
        Member findMember = em.createQuery(qlString, Member.class)
                .setParameter("username", "member1")
                .getSingleResult();

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

JPQL은 쿼리문에서 "usernameeeee" 이라는 오타를 내어도 실행시키기 전까지는 알 수 없다. (최악의 에러)

Querydsl 코드

    @Test
    public void startQuerydsl() {
        JPAQueryFactory queryFactory = new JPAQueryFactory(em);
        QMember m = new QMember("m");   // 어떤 QMember인지 구분하는 이름을 줌

        Member findMember = queryFactory
                .select(m)
                .from(m)
                .where(m.username.eq("member1"))
                .fetchOne();

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

    }

JPQL과 차이점은 파라미터 바인딩이 필요없다는 것이다.

그리고 JPQL과 다르게 "usernameee"이라는 오타를 내면 컴파일 시점에 바로 잡을 수 있기 때문에 좋다.

  • EntityManagerJPAQueryFactory 생성
  • Querydsl은 JPQL 빌더
  • JPQL: 문자(실행 시점 오류를 알 수 있음), Querydsl: 코드(컴파일 시점 오류를 알 수 있음)
  • JPQL: 파라미터 바인딩을 직접, Querydsl: 파라미터 바인딩 자동 처리

⚠️ 참고로 JPAQueryFactory를 필드로 제공하면 동시성 문제는 어떻게 되나?

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


[2] 기본 Q-Type 활용

Q클래스 인스턴스를 사용하는 두 가지 방법

  • QMember qMember = new QMember("m");: 별칭 직접 지정
  • QMember qMember = QMember.member;: 기본 인스턴스 사용

(querydsl은 결국 JPQL의 빌더 역할을 하는 것이라 querydsl은 곧 jpql이기 때문에)
위처럼 실행되는 JPQL을 보기 위해서는 아래와 같은 코드를 application.yml에 추가해주면 된다.

spring.jpa.properties.hibernate.use_sql_comments: true

⚠️ 참고로, 같은 테이블을 조인해야 하는 경우가 아니라면 기본 인스턴스를 사용하도록!


[3] 검색 조건 쿼리

    // 기본 검색 쿼리
    @Test
    public void search() {
        Member findMember = queryFactory
                .selectFrom(member)
                .where(member.username.eq("member1")
                        .and(member.age.eq(10)))    // 이름이 member1이면서 나이가 10살인 사람을 조회해라
                .fetchOne();

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

검색 조건은 .and(), .or()를 메소드 체인으로 연결할 수 있다.

또한, select, fromselectFrom으로 합칠 수 있다.

JPQL이 제공하는 모든 검색 조건을 제공한다.

이외에도 등등,,

아래와 같이 AND 조건을 파라미터로 처리할 수 있다.

강사님은 깔끔해서 이 방식을 더 좋아하심🙃

        Member findMember = queryFactory
                .selectFrom(member)
                .where( // 여기 안에서 여러개를 넘기면 걔네가 다 .and로 엮임
                        member.username.eq("member1"),
                        member.age.eq(10)
                )    // 이름이 member1이면서 나이가 10살인 사람을 조회해라
                .fetchOne();

where()파라미터로 검색 조건을 추가하면 AND 조건이 추가된다.

(이 경우 null 값은 무시되며 메소드 호출을 활용해 동적 쿼리를 깔끔하게 만들 수 있다.)


[4] 결과 조회

    @Test
    public void resultFetch() {
//        List<Member> fetch = queryFactory
//                .selectFrom(member)
//                .fetch();
//
//        Member fetchOne = queryFactory
//                .selectFrom(QMember.member)
//                .fetchOne();    // 단건조회
//
//        Member fetchFirst = queryFactory
//                .selectFrom(member)
//                .fetchFirst();  // limit(1) + fetchOne()

        QueryResults<Member> results = queryFactory
                .selectFrom(member)
                .fetchResults();    // 페이징 정보 포함, total count 쿼리 추가 실행

        results.getTotal();
        List<Member> content = results.getResults();

        long total = queryFactory
                .selectFrom(member)
                .fetchCount();
    }
  • fetch(): 리스트 조회, 데이터 없으면 빈 리스트를 반환
  • fetchOne(): 단건 조회
    - 결과가 없으면 null
    - 결과가 둘 이상이면 com.querydsl.core.NonUniqueResultException
  • fetchFirst(): = limit(1).fetchOne()
    limit(1)을 걸면서 fetchOne()을 하는
  • fetchResults(): 페이징 정보 포함, total count 쿼리 추가 실행
  • fetchCount(): count 쿼리로 변경해 count 수 조회

profile
개발 바보 이사 중

0개의 댓글