2024.05.04
[책 - 스프링 부트 핵심 가이드]
환경 - 스프링부트 2.5.6, 자바 JDK11, Gradle 프로젝트
스프링 부트 핵심 가이드 QueryDSL 연습 깃허브
plugins {
// QueryDSL 에서 JPAAnnotationProcessor 사용을 위한 APT 플러그인 추가
id 'com.ewerk.gradle.plugins.querydsl' version '1.0.10'
}
dependencies {
/*
QueryDSL 관련 의존성 추가
querydsl-jpa: QueryDSL JPA 라이브러리
querydsl-apt: 쿼리 타입(Q클래스)을 생성할 때 필요한 라이브러리
apt(annotation processing tool)
- 어노테이션으로 정의된 코드를 기반으로 새로운 코드를 생성하는 기능
*/
implementation 'com.querydsl:querydsl-jpa'
implementation 'com.querydsl:querydsl-apt'
}
/*
에러 Unable to load class ''. 가 생겨서 아래 추가
*/
// QueryDSL이 생성한 소스 코드의 디렉토리를 지정하는 변수, 기본적으로 'src/main/generated'로 설정
def querydslSrcDir = 'src/main/generated'
// QueryDSL 플러그인의 설정을 정의
querydsl {
// 사용할 QueryDSL 라이브러리를 지정
library = "com.querydsl:querydsl-apt"
// JPA를 사용할 것인지 여부를 나타내며, 여기서는 true로 설정되어 있어 JPA를 사용
jpa = true
// QueryDSL이 생성한 소스 코드를 저장할 디렉토리를 지정
querydslSourcesDir = querydslSrcDir
}
/*
에러 Unable to load class 'com.querydsl.apt.jpa.JPAAnnotationProcessor' 가 생겨서 아래 추가
*/
// 의존성 구성(configurations)을 정의
configurations {
// QueryDSL의 의존성을 컴파일 클래스패스에 추가
querydsl.extendsFrom compileClasspath
// developmentOnly 및 runtimeClasspath에 대한 설정
developmentOnly
runtimeClasspath {
extendsFrom developmentOnly
}
}
// 소스 세트(source set)를 구성
// main 소스 세트에 QueryDSL이 생성한 소스 코드 디렉토리를 추가
sourceSets {
main {
java {
srcDirs = ['src/main/java', querydslSrcDir]
}
}
}
// QueryDSL을 컴파일하는 옵션을 정의
compileQuerydsl{
// 어노테이션 프로세서 경로를 설정합니다. 여기서는 QueryDSL의 configurations.querydsl을 사용
options.annotationProcessorPath = configurations.querydsl
}
.gitignore에 추가
- Git이 해당 디렉토리의 파일을 추적하지 않도록 설정합니다.
- 보통 generated 또는 build와 같은 디렉토리는 소스 코드의 생성물이나 빌드 과정에서 생성되는 파일들을 담는 공간입니다. 이러한 파일들은 소스 코드 저장소에 포함시키지 않는 것이 일반적입니다.
### Custom ### src/main/generated/
@PersistenceContext
EntityManager entityManager;
@Test
@DisplayName("JPAQuery를 활용한 QueryDSL 테스트 코드")
void queryDslTest() {
// QueryDSL을 사용하기 위해서 JPAQuery 객체를 사용 - JPAQuery는 엔티티 매니저(entityManager)를 활용해 생성
JPAQuery<Product> query = new JPAQuery<>(entityManager);
QProduct qProduct = QProduct.product;
// JPAQuery는 빌더 형식으로 쿼리 작성
List<Product> productList = query
.from(qProduct)
.where(qProduct.name.eq("펜"))
.orderBy(qProduct.price.asc())
.fetch();
// List 타입으로 값을 리턴받기 위해서 fetch() 메서드 사용
// - 4.0.1 이전 버전의 QueryDSL을 설정한다면 list() 메서드 사용
for (Product product : productList) {
System.out.println("--------------------");
System.out.println();
System.out.println("Product Number" + product.getNumber());
System.out.println("Product Name" + product.getName());
System.out.println("Product Price" + product.getPrice());
System.out.println("Product Stock" + product.getStock());
System.out.println();
System.out.println("--------------------");
}
}
@Test
@DisplayName("JPAQueryFactory를 활용한 QueryDSL 테스트 코드")
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("--------------------");
System.out.println();
System.out.println("Product Number" + product.getNumber());
System.out.println("Product Name" + product.getName());
System.out.println("Product Price" + product.getPrice());
System.out.println("Product Stock" + product.getStock());
System.out.println();
System.out.println("--------------------");
}
}
@Test
@DisplayName("JPAQueryFactory를 활용한 QueryDSL 테스트 코드")
void queryDslTest3() {
JPAQueryFactory jpaQueryFactory = new JPAQueryFactory(entityManager);
QProduct qProduct = QProduct.product;
List<String> productList = jpaQueryFactory
.select(qProduct.name)
.from(qProduct)
.where(qProduct.name.eq("펜"))
.orderBy(qProduct.price.asc())
.fetch();
for (String product : productList) {
System.out.println("--------------------");
System.out.println("Product Name" + product);
System.out.println("--------------------");
}
List<Tuple> tupleList = jpaQueryFactory
.select(qProduct.name, qProduct.price)
.from(qProduct)
.where(qProduct.name.eq("펜"))
.orderBy(qProduct.price.asc())
.fetch();
for (Tuple product : tupleList) {
System.out.println("--------------------");
System.out.println("Product Name" + product.get(qProduct.name));
System.out.println("Product Price" + product.get(qProduct.price));
System.out.println("--------------------");
}
}
/**
* QueryDSL 컨피그 파일 생성
*/
@Configuration
public class QueryDSLConfiguration {
@PersistenceContext
EntityManager entityManager;
// JPAQueryFactory 객체를 @Bean 객체로 등록해두면 매번 JPAQueryFactory를 초기화하지 않고 스프링 컨테이너에서 가져다 쓸 수 있음
@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(entityManager);
}
}
@Autowired
JPAQueryFactory jpaQueryFactory;
@Test
@DisplayName("JPAQueryFactory 빈을 활용한 테스트 코드")
void queryDslTest4() {
QProduct qProduct = QProduct.product;
List<String> productList = jpaQueryFactory
.select(qProduct.name)
.from(qProduct)
.where(qProduct.name.eq("펜"))
.orderBy(qProduct.price.asc())
.fetch();
for (String product : productList) {
System.out.println("--------------------");
System.out.println("Product Name" + product);
System.out.println("--------------------");
}
}
쿼리 반환 메서드 참고
- List fetch() : 조회 결과를 리스트로 반환
- T fetchOne() : 단 건의 조회 결과를 반환
- T fetchFirst() : 여러 건의 조회 결과 중 1건을 반환
- 내부 로직을 살펴보면 '.limit(1).fetchOne()으로 구현돼 있음
- Long fetchCount() : 조회 결과의 개수를 반환
- QueryResult fetchResults() : 조회 결과 리스트와 개수를 포함한 QueryResults를 반환
// 예시 List<String> productList = jpaQueryFactory .select(qProduct.name) .from(qProduct) .where(qProduct.name.eq("펜")) .orderBy(qProduct.price.asc()) .fetch();