QueryDSL 소개 및 설정방법
실전! Querydsl - 인프런 | 강의
Querydsl의 기초부터 실무 활용까지, 한번에 해결해보세요!, - 강의 소개 | 인프런...
www.inflearn.com
[gradle] 그레이들 Annotation processor 와 Querydsl - I'm honeymon(JiHeon Kim).
이 글에서 다룰 예정인 ‘Querydsl’과 ‘Annotation processor’ 에 관한 내용도, 스프링 부트를 버전업하는 과정에서 겪게 된다. 사내 개발기기 교체주기가 되어 새로운 맥북을 받고 스프링 부트 버전
honeymon.io
소개
QueryDSL은 JPA에서 동적쿼리를 생성할 때 사용하는 JPQL의 Bulider 라이브러리이다.
QueryDSL을 사용하는 경우 JPQL을 사용하는 것에 비해서 다음과 같은 이점을 얻을 수 있다.
- JPQL처럼 문자열으로 작성하지 않기 때문에, IDE의 도움을 받아 쿼리를 작성할 수 있다.
- Compile 시점에 Type-Check가 하기 때문에 RunTime 시점에 해당 예외가 발생하지 않는다.
- 동적쿼리를 쉽게 해결가능하다
JPQL과 QueryDSL 사용방법 비교
String qlString = "select m from Member m " +
"where m.username = :username";
Member findMember = em.createQuery(qlString, Member.class)
.setParameter("username", "member1")
.getSingleResult();
JPAQueryFactory queryFactory = new JPAQueryFactory(em);
QMember m = new QMember("m");
Member findMember = queryFactory.select(m)
.from(m)
.where(m.username.eq("member1"))
.fetchOne();
위의 코드 처럼 QueryDSL은 JPAqueryFactory와 QMember객체를 사용하여 쿼리를 생성한다. 여기서 QMember 객체를 QType 객체라고 하며, QueryDSL에서는 QType 객체를 생성하기위한 QType 클래스를 별도로 생성해주어야 한다.
Gradle 설정
QType 클래스를 일일히 생성할 수는 없으므로 gradle 스크립트를 작성하여 생성하게 되는데,
이러한 스크립트를 작성하는 번거로움을 해소하기 위해 아래 gradle 플러그인을 사용하여 build.gradle파일을 설정할 수 있다.
plugins {
//querydsl 플러그인 추가
id 'com.ewerk.gradle.plugins.querydsl' version '1.0.10'
}
플러그인 추가 후 com.querydsl:querydsl-jpa 라이브러리와 com.querydsl:querydsl-apt 라이브러리를 implementation 해준다.
queryDslVersion변수 선언 없이 버전을 5.0.0으로 하드코딩해도 무방하다.
buildscript {
ext {
queryDslVersion = "5.0.0" // queryDsl 버전 변수 선언
}
}
dependencies {
implementation "com.querydsl:querydsl-jpa:${queryDslVersion}" // querydsl jpa 라이브러리
implementation "com.querydsl:querydsl-apt:${queryDslVersion}" // Q파일 생성 라이브러리
}
QueryDSL QType Class 생성 폴더를 지정해주고, 프로젝트에서 해당 클래스 파일을 사용할 수 있도록 sourceSet 설정해준다.
def querydslDir = "$buildDir/generated/querydsl"
querydsl {
jpa = true
querydslSourcesDir = querydslDir
}
sourceSets {
main.java.srcDir querydslDir
}
gradle 플러그인 'com.ewerk.gradle.plugins.querydsl' 은 2018년 7월에 출시된 1.0.10 버전 이후로 업데이트 되지 않는다.
따라서 Annotation Processor기능을 반영한 gradle 5.x 버전에서는 정상작동 하도록 추가 설정을 해야하며 gradle 6.x 버전 에서는 compile로 선언된 JPA dependency에 접근하기 위해 querydsl이 compileClassPath를 상속하도록하는 설정을 포함시켜야한다.
// querydsl-apt에 있는 AnnotationProcessor의 경로를 설정해준다. gradle 5.X 이상
compileQuerydsl{
options.annotationProcessorPath = configurations.querydsl
}
// compile 로 걸린 JPA 의존성에 접근하기 위해 설정한다. gradle 6.x 이상
configurations {
querydsl.extendsFrom compileClasspath
}
아래는 위에내용을 합친 최종 설정이며, 설정 적용 후 Reload Gradle Projdct 를 실행 후
Gradle Tasks -> other -> compileQuerydsl을 실행시켜주면 QType Class가 bulid/generated/querydsl 폴더에 생성된다.
buildscript {
ext {
queryDslVersion = "5.0.0" // queryDsl 버전 변수 선언..
}
}
plugins {
//querydsl 플러그인 추가
id 'com.ewerk.gradle.plugins.querydsl' version '1.0.10'
}
...
dependencies {
implementation "com.querydsl:querydsl-jpa:${queryDslVersion}" // querydsl jpa 라이브러리
implementation "com.querydsl:querydsl-apt:${queryDslVersion}" // Q파일 생성 라이브러리
}
...
//==querydsl 설정 추가==//
def querydslDir = "$buildDir/generated/querydsl"
querydsl {
jpa = true
querydslSourcesDir = querydslDir
}
sourceSets {
main.java.srcDir querydslDir // 소스폴더에 Q파일 생성폴더를 넣어준다.
}
// querydsl-apt에 있는 AnnotationProcessor의 경로를 설정해준다. gradle 5.X 이상
compileQuerydsl{
options.annotationProcessorPath = configurations.querydsl
}
// compile 로 걸린 JPA 의존성에 접근하기 위해 설정한다. gradle 6.x 이상
configurations {
querydsl.extendsFrom compileClasspath
}
JPAQueryFactory Spring Bean 등록
아래 Configuration 설정을 통해 JPAQueryFactory를 의존성 주입 받아 사용할 수 있다.
JPAQueryFactory는 EntityManager를 통해 생성되며 스프링 프레임워크는 여러 쓰레드에서 동시에 EntityManager에 접근해도
트랜잭션 마다 별도의 영속성 컨텍스트를 제공하므로 동시성 문제는 걱정하지 않아도 된다.
@Configuration
public class QueryDslConfiguration {
@PersistenceContext
private EntityManager entityManager;
@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(entityManager);
}
}
의존성 주입을 통해 JPAQueryFactory를 필드로 선언하여 구현한 코드
@Repository
@RequiredArgsConstructor
public class MemberRepository {
private final JPAQueryFactory queryFactory;
public Member findByUserName(String userName) {
QMember m = new QMember("m");
Member findMember = queryFactory.select(m)
.from(m)
.where(m.userName.eq(userName))
.fetchOne();
return findMember;
}
}