최근 Spring boot + jpa 로 프로젝트 진행하다 jpa 인터페이스 네이밍 방식의 단점을 느끼고 Query DSL을 도입하면서 정리한 글
기본 제공 @Query와 인터페이스 메서드 네이밍 방식은 한계가 있고 가독성도 안좋음
// 쿼리가 길어질수록 더러워짐
@Query(value="UPDATE Work W SET W.workGroup = :workGroup WHERE W.workSeq = :workSeq", nativeQuery = false)
// 네이밍이 길어질수록 더러워짐
Page<Work> findByWorkGroup_WorkGroupNmContainingAndWorkNmContaining(String workGroupNm, String workNm, Pageable pageable);
1. Gradle 설정
많은 기술블로그에 나와있는 방식은 Gradle의 annotationProcessor 지원 전의 내용이다 본 글은 5 이후의 annotationProcessor를 사용하여 진행한다. 장점으로는 플러그인에 의존하지 않는다, 좀더 깔끔해진다.
buildscript {
ext {
queryDslVersion = "4.4.0"
}
}
plugins {
id "org.springframework.boot" version "2.4.0-SNAPSHOT"
id "io.spring.dependency-management" version "1.0.9.RELEASE"
id "java"
}
group = "kr.taeu.workorder"
version = "0.0.1-SNAPSHOT"
sourceCompatibility = "1.8"
repositories {
mavenCentral()
maven { url "https://repo.spring.io/milestone" }
maven { url "https://repo.spring.io/snapshot" }
}
dependencies {
implementation "org.springframework.boot:spring-boot-starter-data-jpa"
implementation "org.springframework.boot:spring-boot-starter-web"
implementation "org.springframework.boot:spring-boot-devtools"
// QueryDSL
implementation ("com.querydsl:querydsl-jpa:${queryDslVersion}")
annotationProcessor ("com.querydsl:querydsl-apt:${queryDslVersion}:jpa")
testImplementation ("com.querydsl:querydsl-jpa:${queryDslVersion}")
testAnnotationProcessor ("com.querydsl:querydsl-apt:${queryDslVersion}:jpa")
// Lombok
implementation "org.projectlombok:lombok"
annotationProcessor ("org.projectlombok:lombok")
testImplementation ("org.projectlombok:lombok")
testAnnotationProcessor ("org.projectlombok:lombok")
runtimeOnly "com.h2database:h2"
testImplementation "org.springframework.boot:spring-boot-starter-test"
}
test {
useJUnitPlatform()
}
플러그인을 사용할때와는 다르게 코드가 매우 깔끔해졌다. 중요한 부분은 QueryDSL 주석부분과 맨위의 버전변수 부분이다. 이외엔 기타 라이브러리 설정이니 무시하자.
2. Java Config
@EnableJpaAuditing
@Configuration
public class DatabaseConfig {
...
@Bean
public JPAQueryFactory jpaQueryFactory(EntityManager em) {
return new JPAQueryFactory(em);
}
...
}
JPAQueryFactory를 bean으로 등록하는것으로 끝
3. 사용법
domain에 대한 repository를 생성해주자 아래는 테스트를 위한 JpaRepository
public interface WorkGroupRepository extends JpaRepository<WorkGroup, Long> {}
아래는 Querydsl Repository이다.
package kr.taeu.workorder.domain.workgroup.repository;
import static kr.taeu.workorder.domain.workgroup.domain.QWorkGroup.workGroup;
...
@Repository
@RequiredArgsConstructor
public class WorkGroupRepositorySupport {
private final JPAQueryFactory queryFactory;
public List<WorkGroup> findByWorkGroupNm(String name) {
return queryFactory.selectFrom(workGroup)
.where(workGroup.workGroupNm.containsIgnoreCase(name))
.fetch();
}
}
JPAQueryFactory는 Bean으로 위에서 등록한걸 주입받는다. 중요한건 맨위의 정적 import를 빼먹지말자.
4. 테스트
@ExtendWith(SpringExtension.class)
@SpringBootTest
public class QuerydslTest {
@Autowired
private WorkGroupRepository workGroupRepository;
@Autowired
private WorkGroupRepositorySupport workGroupRepositorySupport;
@AfterEach
public void clean() throws Exception {
workGroupRepository.deleteAllInBatch();
}
@Test
public void querydsl_기본_테스트() {
// given
String workGroupNm = "테스트그룹1";
workGroupRepository.save(new WorkGroup(workGroupNm));
workGroupNm = "테스트그룹1-1";
workGroupRepository.save(new WorkGroup(workGroupNm));
workGroupNm = "테스트그룹2";
workGroupRepository.save(new WorkGroup(workGroupNm));
// when
List<WorkGroup> workGroups = workGroupRepositorySupport.findByWorkGroupNm("그룹1");
// then
Assertions.assertTrue(workGroups.size() == 2);
Assertions.assertEquals(workGroups.get(0).getWorkGroupNm(), "테스트그룹1");
}
}
테스트 데이터 3개를 생성하고, Querydsl로 작성한 findByWorkGroupNm 메서드로 조회하여 결과를 확인
아주 쉽게 설정부터 테스트까지 완료했다.
'Spring' 카테고리의 다른 글
Spring Cloud Gateway - API Gateway 맛보기 (5) | 2020.10.19 |
---|---|
Spring Security OAuth2 - Authorization endpoint (0) | 2020.10.07 |
Spring Jpa - Paging api 처리하기 (0) | 2020.09.18 |
Spring Data Jpa Auditing (0) | 2020.07.29 |
ApplicationContext와 Singleton (0) | 2020.07.29 |