본문 바로가기

Spring 정리/Querydsl

JPA & JPQL & QueryDsl

728x90
반응형

개요

 


JPA, JPQL, QueryDsl을 사용하여 조회하는 방법을 비교해봅니다.

 

 

조회 방식 코드로 테스트해보기


상황은 Team과 Member가 1:N으로 연관관계를 가지고 있다고 가정합니다. EntityManager를 통해 Team과 Member를 생성합니다.  N쪽에 연관관계의 주인이기 때문에 FK를 가집니다. JPA 설정에서 주의 할 것은, @ManyToOne 연관관계에서 fetch를 LAZY로 설정해야 합니다.(default는 EAGER입니다.)

 

 

JPA 기본 데이터 테스트


순수 JPA로, em.createQuery()를 사용해서 쿼리를 만들 수 있습니다. persist로 영속성 컨텍스트에 넣고, flush로 반영하고, 마지막에는 clear로 영속성 컨텍스트에 담긴 모든 것을 초기화합니다.

 

@SpringBootTest
@Transactional
@Commit
class MemberTest {
	@PersistenceContext
	EntityManager em;
    
	@Test
	public void testEntity() {
		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);
        
		//초기화
		em.flush();
		em.clear();
        
		//확인
		List<Member> members = em.createQuery("select m from Member m",Member.class)
				.getResultList();
		for (Member member : members) {
			System.out.println("member=" + member);
			System.out.println("-> member.team=" + member.getTeam());
		}
	}
}

 

 

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();
	Assertions.assertThat(findMember.getUsername()).isEqualTo("member1");
}

 

JQPL은 실제 쿼리문을 작성하듯이 만들어야 합니다. 직관적인 SQL처럼 사용할 수 있지만 값을 넣을 때 : 를 사용해야 하고 setParameter()로 설정해야 합니다.

 

QueryDsl


@Test
public void startQuerydsl() {
	//member1을 찾아라.
	JPAQueryFactory queryFactory = new JPAQueryFactory(em);
	QMember m = new QMember("m");
	Member findMember = queryFactory
			.select(m)
			.from(m)
			.where(m.username.eq("member1"))//파라미터 바인딩 처리
			.fetchOne();
	Assertions.assertThat(findMember.getUsername()).isEqualTo("member1");
}

 

  • QueryDSL의 장점

1. Querydsl의 경우 JPQL에 비해서 SQL 쿼리문이 더 직관적입니다. select, from , where가 있습니다.

2. JPQL은 문법 오류가 런타임에 발견되지만, Querydsl은 컴파일에 발견됩니다.

3. Querydsl은 파라미터 바인딩을 자동으로 해결해줍니다.

4. 코드 assitance(자동완성) 기능도 제공해서 빠른 생산성이 가능합니다.

 

  • Querydsl의 사용법 특징

1. queryFactory로 시작합니다.

2. fromQMember의 객체가 와야합니다.

3. 파라미터 바인딩도 QMember의 객체에서 변수명에 접근해서 설정합니다. 

 

 

 

QueryDsl 1차 개선


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

	JPAQueryFactory queryFactory;

	@BeforeEach
	public void before() {
		queryFactory = new JPAQueryFactory(em);
		...
	}

	@Test
	public void startQuerydsl() {
		QMember m = QMember.member;
		Member findMember = queryFactory
				.select(m)
				.from(m)
				.where(m.username.eq("member1"))//파라미터 바인딩 처리
				.fetchOne();
		Assertions.assertThat(findMember.getUsername()).isEqualTo("member1");
	}
}

 

변경사항은 다음과 같습니다.

 

1.QueryFactory를 지역변수에서 전역변수로 정의합니다.

QeuryFactory가 전역변수로 선언되어 동시성 문제를 걱정할 수 있는데, 동시성 문제는 JPAQueryFactory를 생성할 때 제공하는 EntityManager(em)에 달려있습니다.  여러 쓰레드에 동시에 같은 EntityManager에 접근해도, 트랜잭션 마다 별도의 영속성 컨텍스트를 제공하기 때문에 문제가 발생하지 않습니다.

 

2. new QMember("m")QMember.member로 개선합니다.

QMember파일 내에 이미 정의되어 있으므로 간단히 QMember.member사용이 가능합니다.

QMember 파일 내에 다음과 같이 정의되어 있습니다.

 

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

 

 

 

 

QueryDsl 2차 개선


QMember.member를 변수에 정의하지 말고 직접 사용합니다.

 

	@Test
	public void startQuerydsl() {
		Member findMember = queryFactory
				.select(QMember.member)
				.from(QMember.member)
				.where(QMember.member.username.eq("member1"))//파라미터 바인딩 처리
				.fetchOne();
		Assertions.assertThat(findMember.getUsername()).isEqualTo("member1");
	}

 

다소 지저분할 수 있는 QMember.member에서 Alt+Enter를 클릭하여 Add on-demand static import for로 좀 더 간소화할 수 있습니다.

 

 

 

최종적으로 깔끔하게 정리된 코드입니다.

 

import static com.example.querydsl.domain.QMember.*;

@Test
public void startQuerydsl() {
	Member findMember = queryFactory
			.select(member)
			.from(member)
			.where(member.username.eq("member1"))//파라미터 바인딩 처리
			.fetchOne();
	Assertions.assertThat(findMember.getUsername()).isEqualTo("member1");
}

 

콘솔로 쿼리문 확인하기


Querydsl은 결국 JPQL을 실행시키는 것과 같은 것인데, Querydsl로 실행할 때 JPQL 쿼리문을 콘솔에 찍고 싶다면 application.yml에 use_sql_comments: true를 추가하면 됩니다.

 

spring:  
  jpa:
    hibernate:
      ddl-auto: create
    properties:
      hibernate:
  # show_sql: true
        format_sql: true
        use_sql_comments: true

 

아래 빨간색 박스에 주석달린 부분이 JPQL 실행 쿼리입니다.

 

 

혹여나 같은 테이블을 조인해야 하는 경우에는 다음과 같이 alias를 다르게 설정해주어야 한다.

 

@Test
public void startQuerydsl() {
    QMember m1 = new QMember("m1");
    QMember m2 = new QMember("m2");
    ...
}

 

 

* 출처

Inflearn : 실전! Querydsl 강의

728x90
반응형

'Spring 정리 > Querydsl' 카테고리의 다른 글

순수 JPA와 Querydsl  (0) 2022.02.13
QueryDsl 수정, 삭제  (0) 2022.02.13
Querydsl 동적쿼리  (0) 2022.02.13
QueryDsl 프로젝션 문법  (0) 2022.02.13
QueryDsl 기본문법  (0) 2022.02.12