QueryDSL 간단하게 사용

코딩하는 하늘토끼·2023년 6월 21일

스프링부트 공부

목록 보기
13/15

프로젝트 환경

종류환경
IDEIntellij IDEA 2023.1.2 (Ultimate Edition)
언어SpringBoot 3.1.0
타입Gradle - Groovy
JDKcorretto-17
패키지 생성Jar
버전관리Github

의존성

종류이름
WebSpring web
Developer ToolsSpring Processor / Lombok
SQLSpring Data JPA / MySQL Driver
기타OpenAPI : 2.0.2 / Gson : 2.10.1 / Spring test / Jacoco : 0.8.7 / queryDSL : 5.0.0

설정

build.gradle

dependencies {
    implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
    annotationProcessor 'com.querydsl:querydsl-apt:5.0.0:jakarta'
    annotationProcessor "jakarta.annotation:jakarta.annotation-api"
    annotationProcessor "jakarta.persistence:jakarta.persistence-api"
    sourceSets {
        main {
            java {
                srcDirs = ["$projectDir/src/main/java", "$projectDir/build/generated"]
            }
        }
    }
    clean {
        delete file('src/main/generated')
    }
}

1.기본적인 QueryDSL 사용하기

코드

test/data/repository/ProductRepositoryTest.java

package com.springboot.hello.data.repository;

import com.querydsl.jpa.impl.JPAQuery;
import com.springboot.hello.data.entity.Product;
import com.springboot.hello.data.entity.QProduct;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;

import java.util.List;

@DataJpaTest    // 기본적으로 내장된 메모리 데이터베이스를 이용해 테스트를 실행(환경은 내장/테스트는 물리 불일치 에러 발생)
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) // 데이터소스를 내장으로 교체하지 않음
@TestInstance(TestInstance.Lifecycle.PER_CLASS) //라이프 사이클을 클래스 단위로 설정, 라이프 사이클을 클래스 단위로 지정해 놓으면 @BeforeAll, @AfterAll 어노테이션을 static method가 아닌 곳에서도 사용가능
public class ProductRepositoryTest {

    @PersistenceContext
    EntityManager entityManager;

    @Autowired
    public ProductRepository productRepository;

    @BeforeAll
    void setup() {
        Product product1 = new Product();
        product1.setName("펜");
        product1.setPrice(2000);
        product1.setStock(100);

        Product product2 = new Product();
        product2.setName("펜");
        product2.setPrice(3000);
        product2.setStock(200);

        Product product3 = new Product();
        product3.setName("펜");
        product3.setPrice(1000);
        product3.setStock(300);

        productRepository.save(product1);
        productRepository.save(product2);
        productRepository.save(product3);
    }

    @Test
    void queryDslTest() {

        JPAQuery<Product> query = new JPAQuery<>(entityManager);
        QProduct qProduct = QProduct.product;

        List<Product> productList = query
                .from(qProduct)
                .where(qProduct.name.eq("펜"))
                .orderBy(qProduct.price.asc())
                .fetch();

        for (Product product : productList) {
            System.out.println(product.getNumber() + " " + product.getName() + " " + product.getPrice() + " " + product.getStock());
        }
    }
}

실행 결과


2. JPAQueryFactory를 활용한 QueryDSL 테스트 코드

코드

test/data/repository/ProductRepositoryTest.java

package com.springboot.hello.data.repository;

import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import com.springboot.hello.data.entity.Product;
import com.springboot.hello.data.entity.QProduct;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;

import java.util.List;

@DataJpaTest    // 기본적으로 내장된 메모리 데이터베이스를 이용해 테스트를 실행(환경은 내장/테스트는 물리 불일치 에러 발생)
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) // 데이터소스를 내장으로 교체하지 않음
@TestInstance(TestInstance.Lifecycle.PER_CLASS) //라이프 사이클을 클래스 단위로 설정, 라이프 사이클을 클래스 단위로 지정해 놓으면 @BeforeAll, @AfterAll 어노테이션을 static method가 아닌 곳에서도 사용가능
public class ProductRepositoryTest {

    @PersistenceContext
    EntityManager entityManager;

    @Autowired
    public ProductRepository productRepository;

    @BeforeAll
    void setup() {
        Product product1 = new Product();
        product1.setName("펜");
        product1.setPrice(2000);
        product1.setStock(100);

        Product product2 = new Product();
        product2.setName("펜");
        product2.setPrice(3000);
        product2.setStock(200);

        Product product3 = new Product();
        product3.setName("펜");
        product3.setPrice(1000);
        product3.setStock(300);

        productRepository.save(product1);
        productRepository.save(product2);
        productRepository.save(product3);
    }

    @Test
    void queryDslTest2() {

        JPAQueryFactory jpaQueryFactory = new JPAQueryFactory(entityManager);
        QProduct qProduct = QProduct.product;

        List<Product> productList = jpaQueryFactory.selectFrom(qProduct)
                .where(qProduct.name.eq("펜"))
                .orderBy(qProduct.price.asc())
                .fetch();

        for (Product product : productList) {
            System.out.println(product.getNumber() + " " + product.getName() + " " + product.getPrice() + " " + product.getStock());
        }
    }
}

실행 결과


3.JPAQueryFactory의 select() 메서드 활용 - 단일 반환 값

코드

test/data/repository/ProductRepositoryTest.java

package com.springboot.hello.data.repository;

import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import com.springboot.hello.data.entity.Product;
import com.springboot.hello.data.entity.QProduct;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;

import java.util.List;

@DataJpaTest    // 기본적으로 내장된 메모리 데이터베이스를 이용해 테스트를 실행(환경은 내장/테스트는 물리 불일치 에러 발생)
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) // 데이터소스를 내장으로 교체하지 않음
@TestInstance(TestInstance.Lifecycle.PER_CLASS) //라이프 사이클을 클래스 단위로 설정, 라이프 사이클을 클래스 단위로 지정해 놓으면 @BeforeAll, @AfterAll 어노테이션을 static method가 아닌 곳에서도 사용가능
public class ProductRepositoryTest {

    @PersistenceContext
    EntityManager entityManager;

    @Autowired
    public ProductRepository productRepository;

    @BeforeAll
    void setup() {
        Product product1 = new Product();
        product1.setName("사인펜");
        product1.setPrice(2000);
        product1.setStock(100);

        Product product2 = new Product();
        product2.setName("수성펜");
        product2.setPrice(3000);
        product2.setStock(200);

        Product product3 = new Product();
        product3.setName("모나미펜");
        product3.setPrice(1000);
        product3.setStock(300);

        Product product4 = new Product();
        product4.setName("지우개");
        product4.setPrice(1000);
        product4.setStock(300);

        productRepository.save(product1);
        productRepository.save(product2);
        productRepository.save(product3);
        productRepository.save(product4);
    }

    @Test
    void queryDslTest3() {

        JPAQueryFactory jpaQueryFactory = new JPAQueryFactory(entityManager);
        QProduct qProduct = QProduct.product;

        List<String> productList = jpaQueryFactory
                .select(qProduct.name)
                .from(qProduct)
                .where(qProduct.name.contains("펜"))
                .orderBy(qProduct.price.asc())
                .fetch();

        for (String product : productList) {
            System.out.println(product);
        }
    }
}

실행 결과


4.JPAQueryFactory의 select() 메서드 활용 - 복수 반환 값

코드

test/data/repository/ProductRepositoryTest.java

package com.springboot.hello.data.repository;

import com.querydsl.core.Tuple;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import com.springboot.hello.data.entity.Product;
import com.springboot.hello.data.entity.QProduct;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;

import java.util.List;

@DataJpaTest    // 기본적으로 내장된 메모리 데이터베이스를 이용해 테스트를 실행(환경은 내장/테스트는 물리 불일치 에러 발생)
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) // 데이터소스를 내장으로 교체하지 않음
@TestInstance(TestInstance.Lifecycle.PER_CLASS) //라이프 사이클을 클래스 단위로 설정, 라이프 사이클을 클래스 단위로 지정해 놓으면 @BeforeAll, @AfterAll 어노테이션을 static method가 아닌 곳에서도 사용가능
public class ProductRepositoryTest {

    @PersistenceContext
    EntityManager entityManager;

    @Autowired
    public ProductRepository productRepository;

    @BeforeAll
    void setup() {
        Product product1 = new Product();
        product1.setName("사인펜");
        product1.setPrice(2000);
        product1.setStock(100);

        Product product2 = new Product();
        product2.setName("수성펜");
        product2.setPrice(3000);
        product2.setStock(200);

        Product product3 = new Product();
        product3.setName("모나미펜");
        product3.setPrice(1000);
        product3.setStock(300);

        Product product4 = new Product();
        product4.setName("지우개");
        product4.setPrice(1000);
        product4.setStock(300);

        productRepository.save(product1);
        productRepository.save(product2);
        productRepository.save(product3);
        productRepository.save(product4);
    }

    @Test
    void queryDslTest4() {

        JPAQueryFactory jpaQueryFactory = new JPAQueryFactory(entityManager);
        QProduct qProduct = QProduct.product;

        List<Tuple> productList = jpaQueryFactory
                .select(qProduct.name, qProduct.price)
                .from(qProduct)
                .where(qProduct.name.contains("펜"))
                .orderBy(qProduct.price.asc())
                .fetch();

        for (Tuple product : productList) {
            System.out.println(product.get(qProduct.name) + " " + product.get(qProduct.price));
        }
    }
}

실행 결과


5.JPAQueryFactory 객체를 @Bean 등록

코드

config/QueryDSLConfiguration.java

package com.springboot.hello.config;

import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class QueryDSLConfiguration {

    @PersistenceContext
    EntityManager entityManager;

    @Bean
    public JPAQueryFactory jpaQueryFactory() {
        return new JPAQueryFactory(entityManager);
    }

}

test/data/repository/ProductRepositoryTest.java

package com.springboot.hello.data.repository;

import com.querydsl.core.Tuple;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import com.springboot.hello.data.entity.Product;
import com.springboot.hello.data.entity.QProduct;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

//@DataJpaTest    // 기본적으로 내장된 메모리 데이터베이스를 이용해 테스트를 실행(환경은 내장/테스트는 물리 불일치 에러 발생) -> 엔티티들과 EntityManager 정도만 등록
@SpringBootTest //@Configuration으로 등록한 @Bean을 쓰고싶으면 @SpringBootTest를 써야함
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) // 데이터소스를 내장으로 교체하지 않음
@TestInstance(TestInstance.Lifecycle.PER_CLASS) //라이프 사이클을 클래스 단위로 설정, 라이프 사이클을 클래스 단위로 지정해 놓으면 @BeforeAll, @AfterAll 어노테이션을 static method가 아닌 곳에서도 사용가능
public class ProductRepositoryTest {

    @PersistenceContext
    EntityManager entityManager;

    @Autowired
    public ProductRepository productRepository;

    @Autowired
    JPAQueryFactory jpaQueryFactory;

    @BeforeAll
    void setup() {
        Product product1 = new Product();
        product1.setName("사인펜");
        product1.setPrice(2000);
        product1.setStock(100);

        Product product2 = new Product();
        product2.setName("수성펜");
        product2.setPrice(3000);
        product2.setStock(200);

        Product product3 = new Product();
        product3.setName("모나미펜");
        product3.setPrice(1000);
        product3.setStock(300);

        Product product4 = new Product();
        product4.setName("지우개");
        product4.setPrice(1000);
        product4.setStock(300);

        productRepository.save(product1);
        productRepository.save(product2);
        productRepository.save(product3);
        productRepository.save(product4);
    }

    @Test
    void queryDslTest5() {

        QProduct qProduct = QProduct.product;

        List<Tuple> productList = jpaQueryFactory
                .select(qProduct.name, qProduct.price)
                .from(qProduct)
                .where(qProduct.name.contains("펜"))
                .orderBy(qProduct.price.asc())
                .fetch();

        for (Tuple product : productList) {
            System.out.println(product.get(qProduct.name) + " " + product.get(qProduct.price));
        }
    }
}

실행 결과

0개의 댓글