본문 바로가기
Framework and Tool/JPA

JPA - 객체지향 쿼리 언어 - QueryDSL 시작

by ocwokocw 2021. 8. 16.

- 참조: 자바 ORM 표준 JPA 프로그래밍

- QueryDSL 시작

여태까지 살펴본 Criteria 는 문자열로 작성한 JPQL 과는 다르게 문법 오류를 컴파일에서 잡을 수 있었지만 코드를 살펴보면 직관적이지 않은 부분이 있었다. QueryDSL 은 HQL 을 코드로 작성하기 위한 오픈소스 프로젝트 였지만 현재는 JPA, JDO, JDBC 등 여러 디비 제품군을 지원하고 있다.


- 설정

JPA 카테고리 JPA - 객체지향 쿼리 언어 - 개요 글에서 QueryDSL 설정법을 살펴본적이 있지만 다시 한번 살펴보도록 하자.

 

<dependency>
    <groupId>com.querydsl</groupId>
    <artifactId>querydsl-apt</artifactId>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>com.querydsl</groupId>
    <artifactId>querydsl-jpa</artifactId>
</dependency>

............

<plugin>
    <groupId>com.mysema.maven</groupId>
    <artifactId>apt-maven-plugin</artifactId>
    <version>1.1.3</version>
    <executions>
        <execution>
            <goals>
                <goal>process</goal>
            </goals>
            <configuration>
                <outputDirectory>target/generated-sources/java</outputDirectory>
                <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
            </configuration>
        </execution>
    </executions>
</plugin>

 

pom.xml 에 위와 같이 dependency 와 plugin 을 설정해주어야 한다. dependency 의 querydsl-jpa 는 QueryDSL 을 사용하기 위한 기본적인 라이브러리이며 querydsl-apt 는 Criteria 의 Meta model 과 같이 엔티티를 기반으로 Q-type 이라는 클래스를 생성하기 위해 필요한 라이브러리이다.

 

그 후 콘솔에 mvn compile 을 입력해주면 Q-type 이 생성된다.

 


- 기본 사용

예제를 한번 살펴보자.

 

Member member1 = new Member();

member1.setName("Name#1");
member1.setAge(10);
member1.setAddress(
		new Address("City#1", "Street#1", "Zipcode#1"));
em.persist(member1);

Member member2 = new Member();

member2.setName("Name#2");
member2.setAge(13);
member2.setAddress(
		new Address("City#1", "Street#2", "Zipcode#2"));
em.persist(member2);

Member member3 = new Member();

member3.setName("Name#3");
member3.setAge(32);
member3.setAddress(
		new Address("City#2", "Street#3", "Zipcode#3"));
em.persist(member3);

Member member4 = new Member();

member4.setName("Name#4");
member4.setAge(22);
member4.setAddress(
		new Address("City#2", "Street#4", "Zipcode#4"));
em.persist(member4);

.................

JPAQueryFactory query = new JPAQueryFactory(em);

QMember qMember = new QMember("m");

List<Member> members = query.select(qMember)
	.from(qMember)
	.where(qMember.age.gt(20))
	.orderBy(qMember.age.desc())
	.fetch();

members.forEach(member -> {
	System.out.println("Member age: " + member.getAge());
});

 

위의 코드는 Member 4 명의 엔티티를 영속시킨 후 나이 20 초과조건을 조회한 다음 내림 차순으로 정렬한 예제이다.

 

QueryDSL 을 사용하려면 JPAQueryFactory 에 엔티티매니저를 인자로 넘겨준 후 사용하면 된다. 그 후 query 를 이용해서 JPQL 을 빌딩하는 코드를 잘 살펴보면 느끼는 점이 있을 것이다. Criteria 와는 다르게 상당히 직관적임을 알 수 있다.

 

Hibernate: 
    /* select
        m 
    from
        Member m 
    where
        m.age > ?1 
    order by
        m.age desc */ select
            member0_.member_id as member_i1_6_,
            member0_.insert_datetime as insert_d2_6_,
            member0_.update_datetime as update_d3_6_,
            member0_.city as city4_6_,
            member0_.street as street5_6_,
            member0_.zipcode as zipcode6_6_,
            member0_.age as age7_6_,
            member0_.name as name8_6_ 
        from
            member member0_ 
        where
            member0_.age>? 
        order by
            member0_.age desc
Member age: 32
Member age: 22

 

조회된 결과에서 주석에 나온 JPQL 을 보면 거의 순서도 일치하며 query 를 사용하기 위해 예약어와 표현식을 위하 CriteriaQuery 와 CriteriaBuilder 를 따로 사용하지 않고, query 에서 예약어를 표현하며 Q-type 에서 표현을 사용한다.


- Default Q-Type 생성

기본 예제에서는 QMember 생성시 "m" 문자열 인자를 넘겨주어 별칭을 별도로 지정하였다. 이는 Join 을 사용할 시 별칭을 다르게 사용해야 할 경우가 있기 때문이다. 만약 그렇지 않다면 기본으로 제공하는 Q-Type 을 이용할 수 있다.

 

JPAQueryFactory query = new JPAQueryFactory(em);

QMember qMember = QMember.member;

List<Member> members = query.select(qMember)
		.from(qMember)
		.where(qMember.age.gt(20))
		.orderBy(qMember.age.desc())
		.fetch();

members.forEach(member -> {
	System.out.println("Member age: " + member.getAge());
});

 

QMember.member 를 사용하면 기본제공 Q-Type 을 생성한다. 위의 코드를 실행하면 member1 이라는 별칭으로 JPQL 이 생성되는것을 알 수 있는데 이는 Q-Type 소스를 보면 왜 그런지를 알 수 있다.

 

/**
 * QMember is a Querydsl query type for Member
 */
@Generated("com.querydsl.codegen.EntitySerializer")
public class QMember extends EntityPathBase<Member> {

    private static final long serialVersionUID = -925484204L;

    private static final PathInits INITS = PathInits.DIRECT2;

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

..................

댓글