DevKim

[Querydsl] Querydsl VS. JPQL 본문

JPA

[Querydsl] Querydsl VS. JPQL

on_doing 2021. 7. 21. 19:07
728x90

QueryDSL이란?

  • SQL, JPQL을 코드로 작성할 수 있도록 도와주는 빌더 API
  • JPA 크리테이라에 비해서 편리하고 실용적이다

SQL, JPQL의 문제점

  • SQL, JPQL은 문자열이다. Type-check가 불가능하다.
  • 잘 해봐야 애플리케이션 로딩 시점에 알 수 있다. 컴파일 시점에 알 수 있는 방법이 없다. 
  • 해당 로직 실행 전까지 작동여부 확인을 할 수 없다.
  • 해당 쿼리 '실행 시점'에 오류를 발견한다.

QueryDSL 장점

  • 문자가 아닌 코드로 작성할 수 있다.
  • 컴파일 시점에 문법 오류를 발견할 수 있다.
  • 코드 자동완성(IDE 도움)
  • 단순하고 쉽다. 코드 모양이 JPQL과 거의 비슷하다
  • 동적 쿼리

QueryDSL - 동작원리 쿼리타입 생성

  • Member.java @Entity를 가지고, QMember.java라는 QueryDSL 전용 객체를 만든다.

QueryDSL - 동적 쿼리

  • 사실 QueryDSL을 쓰는 진짜 이유는 동적 쿼리 때문이다.
  • JPQL은 정적 쿼리이다. 문자열을 더하기 해야하는데, 동적쿼리가 들어오면 코드가 복잡하고 길어진다.
  • 원하는 필드만 뽑아서 DTO로 뽑아내는 기능도 QueryDSL이 다 지원한다.
  • 얼른 배워서 써보고 싶다 !!!!!!!!!!!!!!!!!

간단하게 member와 team이 1:다 관계의 양방향 매핑되어있다고 했을 때,

username이 member1인 member를 찾을 때, JPQL로 코드를 작성하면 다음과 같다.

 

[ JPQL ]

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

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

이 로직을 Querydsl로 바꿔보자

 

[ Querydsl ]

 

실행전에 새로 만든 Member와 Team에 대한 Q 파일을 먼저 실행해주고 Q 파일이 생성된 것을 확인해주었다.

./gradlew compileQuerydsl
//Querydsl
@Test
public void startQuerydsl() throws Exception{

     JPAQueryFactory queryFactory=new JPAQueryFactory(em);
     QMember m = new QMember("m");

     Member findMember = queryFactory
             .select(m)
             .from(m)
             .where(m.username.eq("member1")) //파라미터 바인딩 처리
             .fetchOne();

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

}

벌써부터 깔끔한 냄새가 난다.. 이 코드를 조금 더 수정해보자 🏃


[ JPAQueryFactory 필드로 제공하기 ]

- QueryFactory를 생성할 때 EntityManager을 넣어주어야하는데, 이걸 필드로 빼볼까?

 

JPAQueryFactory를 필드로 빼주고, BeforeEach문에서 엔티티 매니저를 넣어줬다.

public class QuerydslBasicTest {

    @Autowired
    EntityManager em;

    JPAQueryFactory queryFactory;

    @BeforeEach
    public void setUp()
    {
        queryFactory=new JPAQueryFactory(em);
        
        ...
        임의의 더미 데이터 생성
    }


    @Test
    public void startQuerydsl() throws Exception{

        QMember m = new QMember("m");

        Member findMember = queryFactory
                .select(m)
                .from(m)
                .where(m.username.eq("member1")) //파라미터 바인딩 처리
                .fetchOne();

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

    }
JPAQueryFactory를 필드로 제공하면 동시성 문제는 어떻게 될까?

 Spring 프레임 워크는 여러 쓰레드에서 동시에 같은 EntityManager에 접근해도,

트랜잭션 마다 별도의 영속성 컨텍스트를 제공하기 때문에, 동시성 문제는 걱정하지 않아도된다.

(EntityManager 자체가 멀티 스레드에 문제가 없게 설계가 되어있다고한다)


[ Q 클래스 인스턴스를 사용하는 3가지 방법 ]

 

1. 별칭을 직접 지정하는 방법

QMember m = new QMember("m");

2. 기본 인스턴스 사용하는 방법

QMember m = QMember.member;

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

Member findMember = queryFactory
                .select(QMember.member)
                .from(QMember.member)
                .where(QMember.member.username.eq("member1")) //파라미터 바인딩 처리
                .fetchOne();

여기서 친절한 인텔리제이의 도움을 받아 static import를 해주자

더더 깔끔해진 것을 볼 수 있다.

Member findMember = queryFactory
                .select(member)
                .from(member)
                .where(member.username.eq("member1")) //파라미터 바인딩 처리
                .fetchOne();

* 같은 테이블을 조인해야 하는 경우가 아니면, 이처럼 기본 인스턴스를 사용하는 것을 권장하신다고한다. *

 

 

JPQL과 Querydsl의 가장 큰 차이는 전자는 문자라는 점이고, 후자는 코드라는 점이다.

즉 Querydsl을 사용하면 컴파일 시점에 모든 에러를 잡아준다. 그게 가장 큰 장점인 것 같다

Querydsl에 대해 더더 자세히 공부해보자!


참고자료

https://www.inflearn.com/course/Querydsl-%EC%8B%A4%EC%A0%84

https://ict-nroo.tistory.com/117

728x90
Comments