querydsl 설정하는 방법

2021.06.29 - [IT/JPA & Hibernate] - [Hibernate] Spring Boot Querydsl 설정하기
querydsl을 쿼리 결과를 엔티티가 아닌 미리 만들어둔 TO 객체에 담아 조회할 수 있습니다.

 

예제 소스

쿼리 결과를 원하는 객체에 담아 조회하기 위해서 Projections 클래스를 이용하며 select메소드의 parameter로 넘겨줍니다.
setter, fields, constructor 방식이 있으며 각각 Projections.bean(), Projections.constructor(), Projections.fields() 메소드를 이용합니다.

그리고 Q클래스의 정의된 멤버변수를 이용해 TO객체와 일대일 대응 시키는 방식으로 데이터를 담습니다.

 

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class MemberTO {
    public MemberTO(Long id) {
        this.id = id;
    }

    public MemberTO(Long id, String account, String name) {
        this.id = id;
        this.account = account;
        this.name = name;
    }

    private Long id;

    private String account;

    private String password;

    private String name;

}
@Test
    public void TO_객체에담아서조회하기_setter() {
        JPAQueryFactory query = new JPAQueryFactory(em);
        QMember qMember = QMember.member;

        //setter 방식 
        //반드시 각 멤버변수의 setter가 존재해야한다.
        MemberTO memberTO = query.select(Projections.bean(
                MemberTO.class,
                qMember.id,
                qMember.name,
                qMember.account
        ))
                .from(qMember)
                .where(qMember.id.eq(1L))
                .fetchOne();
    }

    @Test
    public void TO_객체에담아서조회하기_생성자() {
        JPAQueryFactory query = new JPAQueryFactory(em);
        QMember qMember = QMember.member;

        //constructor 방식
        //반드시 전달하려는 멤버변수의 개수에 맞는 생성자를 미리 선언해두어야 하며 
        //데이터가 잘못 초기화되지 않게 각 멤버변수가 일대일 대응될 수 있게 넣어주어야한다.
        MemberTO memberTO = query.select(Projections.constructor(
                MemberTO.class,
                qMember.id,
                qMember.name,
                qMember.account
        ))
                .from(qMember)
                .where(qMember.id.eq(1L))
                .fetchOne();
    }

    @Test
    public void TO_객체에담아서조회하기_field() {
        JPAQueryFactory query = new JPAQueryFactory(em);
        QMember qMember = QMember.member;

        //fields 방식
        //멤버변수가 private라도 대입이 가능하다. 
        MemberTO memberTO = query.select(Projections.fields(
                MemberTO.class,
                qMember.name,
                qMember.id,
                qMember.account,
                qMember.password
        ))
                .from(qMember)
                .where(qMember.id.eq(1L))
                .fetchOne();
    }

 

1. Querydsl 이란?

JPA를 이용해서 복잡한 쿼리나 동적 쿼리를 작성할 경우 소스코드가 지저분해지는 경우가 많습니다.
이때 Querydsl 라이브러리를 이용해면 쿼리문자가 아니라 자바코드로 쿼리를 작성할 수 있습니다.

 

2. Querydsl의 장점

1. 자바코드로 쿼리를 작성하기 때문에 가독성이 좋아집니다.
2. 문법 오류를 컴파일 시점에서 잡아줍니다.
3. 동적 쿼리를 쉽게 만들 수 있습니다.

 

3. Querydsl 설정하기

테스트 환경은 다음과 같습니다. (버전을 모두 맞춰 주어야합니다!)
spring boot 2.2.2 RELEASE
maven
java 11
h2

1) pom.xml

...

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>com.querydsl</groupId>
            <artifactId>querydsl-apt</artifactId>
            <version>4.1.4</version>
        </dependency>

        <dependency>
            <groupId>com.querydsl</groupId>
            <artifactId>querydsl-jpa</artifactId>
            <version>4.1.4</version>
        </dependency>

        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
 
 ...
    
   <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
            <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>
        </plugins>
    </build>
...
querydsl-apt : Q클래스 생성 라이브러리입니다.
querydsl-jpa : querydsl 관련 라이브러리입니다.

apt-maven-plugin을 이용해서 target/generated-sources/java package에 엔티티 별로 Q클래스를 생성합니다.

 

2) src/main/resources/application.properties

spring.datasource.username=sa
spring.datasource.password=
spring.datasource.url=jdbc:h2:mem:mydb
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.hikari.jdbc-url=jdbc:h2:mem:mydb
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=create
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.show_sql=true
h2 database 설정 및 hibernate 설정을 해줍니다.

 

3) Member.java

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;

@Data
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(unique = true, nullable = false)
    private String account;

    @Column(nullable = false)
    private String password;

    @Column(nullable = false)
    private String name;

}
Member 엔티티를 정의하고 mvn package를 하면 target/generated-sources/java package에 엔티티 별로 Q클래스가 생성됩니다. 
Q클래스는 querydsl을 이용해 엔티티의 컬럼에 접근하거나 다양한 쿼리구문을 작성할 때 사용합니다. 

 

4) MemberTest.java

import com.querydsl.jpa.impl.JPAQueryFactory;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Commit;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;

import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest
@Transactional
@Commit
class MemberTest {

    @Autowired
    private EntityManager em;
    final String name = "member1";

    @BeforeEach
    public void setup(){
        Member member = Member.builder()
                .account("member@email.com")
                .name(name)
                .password("1234")
                .build();

        em.persist(member); // 미리 member를 생성한다.
    }

    @Test
    public void membersSearchTest() {
        JPAQueryFactory query = new JPAQueryFactory(em);
        QMember qMember = QMember.member;


        //querydsl을 이용해 java구문으로 query를 작성한다.
        Member member = query
                .select(qMember)
                .from(qMember)
                .where(qMember.name.eq(name))
                .fetchOne();

        assertEquals(member.getName(), name);
    }

}
Querydsl을 사용하려면 EntityManager객체를 생성자로 받은 JPAQueryFactory 객체가 필요합니다.
위 테스트를 진행하면 다음과 같은 쿼리가 DB로 전송됩니다.

---------------------------------------------------------
   select
        member0_.id as id1_0_,
        member0_.account as account2_0_,
        member0_.name as name3_0_,
        member0_.password as password4_0_ 
    from
        member member0_ 
    where
        member0_.name=?
---------------------------------------------------------

+ Recent posts