Tools & Libraries/Querydsl

[Querydsl] Querydsl 기본문법 1 (JPQL vs Querydsl, Q-Type이란?)

대기업 가고 싶은 공돌이 2024. 8. 29. 03:26

JPQL과 Querydsl로 각각 쿼리 짜보기

    @Autowired
    EntityManager em;

    @BeforeEach
    public void before(){
        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);
    }

 

우선 다음과 같이 테스트에 필요한 데이터들을 BeforeEach를 통해 넣어주었다.

 

member1을 찾아라 /JPQL

@Test
public void startJPQL(){
    //member1을 찾아라
    Member findByJPQL = em.createQuery("select m from Member m where m.username = :username", Member.class)
            .setParameter("username", "member1")
            .getSingleResult();

    Assertions.assertThat(findByJPQL.getUsername()).isEqualTo("member1");
}

 

JPQL에서 member1을 찾기 위해선 다음과 같은 쿼리를 짜야한다.

 

다음과 같이 무사히 테스트에 통과한 걸 확인할 수 있다.

이제 querydsl로 쿼리를 짜보자

 

member1을 찾아라 / querydsl

@Test
public void startQuerydsl(){
    JPAQueryFactory queryFactory = new JPAQueryFactory(em);
    QMember m = new QMember("m");

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

    Assertions.assertThat(findByMember.getUsername()).isEqualTo("member1");
}

querydsl에서는 다음과 같이 코드를 작성할 수 있다.

실행 결과 역시 무사히 통과 되었다.

 

querydsl에서는 우선 entityManager를 JPQQueryFactory에 넘겨줘야 한다.

 

엔티티 매니저가 있어야 데이터를 찾던가 할 수 있기 때문이다.

 

QMember를 만들 땐 별칭을 넣어줘야 하는데, 별칭은 사용하지 않기에 중요하지 않다.

 

이후 쿼리를 작성해주면 되는데, 

여기서 querydsl의 장점 중 하나를 확인할 수 있다.

 

바로 파라미터 바인딩을 하지 않는다는 것이다.

.where(m.username.eq("member1"))

 

이 부분을 보면 알겠지만 그냥 member1을 넘겨줘도 파라미터 바인딩을 자동으로 해준다.

 

query dsl은 prepared statement를 사용하기 때문에,

 

SQL Injection도 방어할 수 있는 보안상의 이점도 있다.

 

또한 jpql은 문자기 때문에 컴파일 시점에 오류를 잡아낼 수 없지만,

querydsl은 Q타입을 통해 자바 코드로 풀어냄으로써 컴파일 시점에 오류를 잡아낼 수 있다.

 

참고로 JPQQueryFactory는 필드로 빼서 미리 앤티티 매니저를 주입시킨 후 사용해도 무방하다.

 

필드로 뺐을 경우 동시성 문제를 걱정할 수 있는데, 스프링에서 현재 트랜잭션에 따라 해당 트랜잭션에

적절하게 앤티티 매니저를 분배하도록 설계돼있기 때문에 동시성 문제는 걱정하지 않아도 된다고 한다.

 

Q-Type

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

  1. QMember qMember = new QMember("m")   // 별칭을 직접 지정하여 사용하기
  2. QMember qMember = QMember.member // 기본 인스턴스 사용하기

위에 테스트 코드를 작성할 때 사용했던 방식처럼 별칭을 직접 지정해서 사용할 수도 있지만,

 

기본 인스턴스를 활용해서 Q타입 클래스를 사용할 수도 있다.

public static final QMember member = new QMember("member1");

 

다음과 같이 QMember 클래스 필드에 상수로 다음과 같이 미리 생성이 돼있는 걸 사용하는 방식이다.

 

2번의 기본 인스턴스를 사용하는 방식보다 더 코드를 간단하게 작성하는 방식이 있다.

 

바로 스태틱 임포트를 사용하는 방식이다.

import static study.querydsl.entity.QMember.member;

 

다음과 같이 스태틱 임포트를 통해 필드값에 바로 접근할 수 있도록 하여

Member findByMember = queryFactory
        .select(member)
        .from(member)
        .where(member.username.eq("member1"))
        .fetchOne();

 

코드를 이렇게까지 줄일 수 있다.

 

셀프 조인을 하는 경우

querydsl에서 같은 테이블을 조인하는 경우에 참고해야할 사항이 있다.

 

같은 테이블을 조인할 때 테이블은 alias를 통해 구분된다.

 

하지만 static import를 통해 만들어진 alias를 사용하게 되면 테이블 구분이 되지 않으므로,

 

기존의 방식 QMember m1 = new QMeber("m1") 을 통해 alias를 직접 정의해준 후에 셀프 조인을 해줘야한다.

 

참고: 김영한 실전! Querydsl