초기에 jpql을 통해 동적쿼리나 Named Query를 작성하는 것을 벗어나서 이제 Querydsl을 사용하도록 변경하려고 한다.
정말로 동적 쿼리를 jpql을 사용하여 문자열로 합쳐서 사용하려니 중복코드와 휴먼에러가 정말 많이 발생한다.
또한 동적 쿼리에 대해 특정 조건이 추가되는 경우에 상당한 정신력을 소모하고 페이징 쿼리인 경우에 count를 조회하는 쿼리도 동일하게 중복해서 작성되기 때문에 매우 생산성이 떨어진다.
Querydsl의 설정은 생각보다 복잡한 편이다. 하나씩 설정해 보자.
Spring boot가 3.0 버전이 나왔다. 3.0 버전으로 진행하고 싶지만 3.0 버전의 default jdk 버전이 17이기에 일단 2.0 버전을 선택하여 만들려고 한다.
Querydsl에 대한 의존성은 추가할 수 없다. 다만 스프링 부트에서 Querydsl의 버전은 관리는 해준다.
버전은 스프링 부트에서 관리해 주기 때문에 별도의 querydsl 버전 설정 없이 그래들로 설정하면 적당한 버전을 받아주는 것 같다. 나는 2.7.8 기준으로 5.0.0으로 받아졌다.
5.0 공식문서를 보니까 JPAAnnotationProcessor가 javax.persistence.Entity 어노테이션을 찾아 query types을 만들어 준다고 한다.
"com.ewerk.gradle.plugins.querydsl" version "1.0.10"
https://plugins.gradle.org/plugin/com.ewerk.gradle.plugins.querydsl
특히 querydsl jpa는 엔티티 클래스를 찾아 Q클래스로 변경하는 스크립트를 작성했어야 했고 이러한 이유로 위의 플러그인을 사용했다고 한다.
Gradle 플러그인 사용하는 이유가 보통 빌드 프로세스 일반 작업을 자동화하고 빌드 로직에 재사용 가능한 코드를 쓰고 싶거나 다른 도구 및 라이브러리 통합하고 빠르게 변화하는 Gradle에 영향받지 않고 일관되게 쓰고 싶어서 인데
해당 플러그인이 2018년 7월 2일에 최신버전으로 마무리된 것으로 보아 사용하지 않는 것이 좋아 보인다.
다른 플러그인이 있나 보았는데 다른 플러그인도 엇비슷했고 특정 블로그에서 삽질한 경험이 있다고 해서 엿보았다.
gradle 4.6에서 Convenient declaration of annotation processor dependencies 이 추가되었다고 한다. 애노테이션 기반 코드 처리성능? 향상 목적으로 나왔다고 한다.
스프링 부트의 과거 버전은 그래이들 버전이 낮아서 해당 플러그인을 사용한 것으로 보이고 현재는 대부분 최신버전을 쓰고 대부분 gradle의 최신버전을 사용할 것이다. (현재 기준 7.6이다.)
https://github.com/querydsl/querydsl/tree/master/querydsl-apt/src/main/java/com/querydsl/apt/jpa
JPAAnnotationProcessor를 사용해서 Q클래스를 생성하는데 이를 4.6 과거에서 JPAAnnotationProcessor를 사용하는 스크립트를 정의하는 게 어려웠다고 한다. 그래서 그래이들 버전 업그레이드마다 작성되는 querydsl 플러그인 스크립트 방법이 매번 달라졌서 버전 관리에 프로젝트가 영향을 받았다.
하지만 업데이트된 Gradle Annotation Processor를 사용하면 Gradle 자체에서 선택하므로 추가 구성 없이 라이브러리 자체에 선언된 적절한 Annotation Processor를 사용할 수 있습니다.
이러한 설정으로 인해 빌드 프로세스가 컴파일 중에 선언된 API를 사용할 수 있도록 보장한다.
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
// queryDSL 설정
implementation "com.querydsl:querydsl-jpa"
implementation "com.querydsl:querydsl-core"
implementation "com.querydsl:querydsl-collections"
annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jpa" // querydsl JPAAnnotationProcessor 사용 지정
annotationProcessor "jakarta.annotation:jakarta.annotation-api" // java.lang.NoClassDefFoundError (javax.annotation.Generated) 대응 코드
annotationProcessor "jakarta.persistence:jakarta.persistence-api" // java.lang.NoClassDefFoundError (javax.annotation.Entity) 대응 코드
}
tasks.named('test') {
useJUnitPlatform()
}
// Querydsl 설정부
def generated = 'src/main/generated'
// querydsl QClass 파일 생성 위치를 지정
tasks.withType(JavaCompile) {
options.getGeneratedSourceOutputDirectory().set(file(generated))
}
// gradle clean 시에 QClass 디렉토리 삭제
clean {
delete file(generated)
}
이거 사용하면 될 것 같다. 테스트를 한번 해보자.
clean도 잘된다.
complie시점에 Q클래스를 만들도록 구성했으니 프로젝트별로 적절한 task 위치로 조정해야겠다.
이제 실제로 Querydsl을 사용해 보자.
Order order = new Order();
em.persist(order);
JPAQueryFactory queryFactory = new JPAQueryFactory(em);
QOrder qOrder = new QOrder("o");
Order result = queryFactory
.selectFrom(qOrder)
.fetchOne();
Assertions.assertThat(result).isEqualTo(order);
처음에 오류가 발생하길래 로그를 보니까 order가 예약어여서 발생하더라. globally_quoted_identifiers 값을 true로 해결했다..
jpa:
hibernate:
ddl-auto: create
properties:
hibernate:
globally_quoted_identifiers: true
format_sql: true
dialect: org.hibernate.dialect.H2Dialect
'데이터 접근 기술' 카테고리의 다른 글
Querydsl 기본(3) (0) | 2023.02.07 |
---|---|
Querydsl 기본(2) (0) | 2023.02.06 |
QueryDSL 적용 방법 알아보기. (0) | 2023.02.06 |
[Querydsl] 기본 (0) | 2023.02.01 |
Hibernate Best Practices (0) | 2023.02.01 |
댓글