[Querydsl] Querydsl 기본문법 1 (JPQL vs Querydsl, Q-Type이란?)
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가지 방법
- QMember qMember = new QMember("m") // 별칭을 직접 지정하여 사용하기
- 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