Query DSL이란?
Spring에서 JPA는 기본적으로 제공하는 CRUD 메서드나 쿼리 메서드 기능이 있는데, 원하는 조건을 만들기 위해서는 복잡하게 로직을 만들 수 있어도, 쿼리 문자열이 상당히 길어진다.
또한 원하는 조건을 얻기 위해 JPQL을 작성하게 되는데, JPQL 문자열에 오타 혹은 문법적 오류 발생 시 로딩에서는 발견할 수 있으나, 그 외에서는 런타임 시점에서 에러가 발생된다.
이 문제들을 해결하기 위해 QueryDSL이 만들어졌다.
이는 정적타입을 이용해 SQL 등 쿼리를 생성해주는 오픈소스 프레임 워크이며
1. 문자가 아닌 JAVA 코드로 쿼리를 작성하여 컴파일 시점에서 문법 오류를 발견 가능.
2. 동적 쿼리 작성
3. 자동 완성 기능 사용 가능
등 장점이 존재한다. 하지만, QueryDSL을 사용하기 위해 적용하는 방법이 어렵다.
Query DSL vs 일반 JPA 함수
장점 | 복잡한 쿼리를 만들 수 있다. | 사용법이 단순하다. |
단점 | 코딩을 더 많이 해야 한다. | 복잡한 쿼리를 만들 수 없다. |
QueryDSL을 적용하는 법을 알아보자.
QueryDSL 적용
// build.gradle에 추가
// querydsl 추가
buildscript {
ext {
queryDslVersion = "5.0.0"
}
}
plugins {
id 'org.springframework.boot' version '2.7.3'
id 'io.spring.dependency-management' version '1.0.13.RELEASE'
id 'java'
//querydsl 추가
id "com.ewerk.gradle.plugins.querydsl" version "1.0.10"
}
group = 'com.ll.exam'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'org.mariadb.jdbc:mariadb-java-client'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
//querydsl 추가
implementation "com.querydsl:querydsl-jpa:${queryDslVersion}"
annotationProcessor "com.querydsl:querydsl-apt:${queryDslVersion}"
}
tasks.named('test') {
useJUnitPlatform()
}
//querydsl 추가 시작
def querydslDir = "$buildDir/generated/querydsl"
querydsl {
jpa = true
querydslSourcesDir = querydslDir
}
sourceSets {
main.java.srcDir querydslDir
}
configurations {
querydsl.extendsFrom compileClasspath
}
compileQuerydsl {
options.annotationProcessorPath = configurations.querydsl
}
QueryDSL은 세부적인 쿼리문까지 작성할 수 있는데, 작성하기 위해서는 QueryRepository에 RepositoryCustom을 상속해주어야 한다.
public interface UserRepository extends JpaRepository<SiteUser, Long>, UserRepositoryCustom {
}
그리고 이 RepositoryCustom은 Interface로 추상 메서드만 구성할 수 있는데, 추상 메서드들을 구상하기 위해 구상 메서드를 만들 수 있는 RepositoryImpl 클래스를 생성해 RepositoryCustom을 implement 해준다.
@RequiredArgsConstructor
public class UserRepositoryImpl implements UserRepositoryCustom {
private final JPAQueryFactory jpaQueryFactory;
}
여기서 JPAQueryFactory가 존재하는데, JpaQueryFactory를 통해 Java로 실제 쿼리문을 작성할 수 있도록 만든다.
해당 객체는 Bean으로 만들어주기 위해 base/AppConfig 클래스를 생성하여 Bean객체로 만들어준다.
@Configuration
public class AppConfig {
@Bean
public JPAQueryFactory jpaQueryFactory(EntityManager entityManager) {
return new JPAQueryFactory(entityManager);
}
}
테스트
테스트를 QueryDSL을 사용해 findById 메서드를 만들어보자.
// UserRepositoryImpl.java
@Override
public SiteUser getQslUser(Long id) {
return jpaQueryFactory
.select(siteUser)
.from(siteUser)
.where(siteUser.id.eq(id))
.fetchOne();
}
JpaQueryFactory로 쿼리문을 작성해 SiteUser객체를 만들어 냈다. 이 객체는 RepositoryCustom에
public interface UserRepositoryCustom {
SiteUser getQslUser(Long id);
}
하나의 추상 메서드로 만들어 놓은 후 생성했으며 UserRepository에 상속되어 있기 때문에 getQslUser 메서드를 Repository를 통해 사용할 수 있다.
@Test
@DisplayName("1번 회원 찾기")
void t2() {
SiteUser u1 = userRepository.getQslUser(1L);
assertThat(u1.getId()).isEqualTo(1L);
assertThat(u1.getUsername()).isEqualTo("user1");
assertThat(u1.getPassword()).isEqualTo("{noop}1234");
assertThat(u1.getEmail()).isEqualTo("user1@test.com");
}
'Spring' 카테고리의 다른 글
[JAVA] JAR과 WAR (0) | 2023.02.06 |
---|---|
[Spring, QueryDSL]기본 테스트 데이터 생성 (0) | 2022.08.29 |
[Spring]기능 - 검색 (0) | 2022.08.25 |
[Spring]기능 - 앵커(anchor) (0) | 2022.08.23 |
[Spring]기능 - 추천 기능 (0) | 2022.08.23 |