DB를 객체지향적으로 다룰 수 있는 JPA를 사용하면 간단한 작업(ex. CURD)을 할 때는 편리하다는 생각을 했습니다. 하지만 테이블들이 많은 환경에서는 join을 해야 하거나 서브쿼리를 만들어야 하는 경우가 많아지고, Query가 점점 길어지면서 JPA만으로는 작업하기 까다롭겠다는 생각을 하였습니다.
이에 대한 해결 방안으로 JPA와 MyBatis를 같이 사용하여 복잡한 쿼리는 MyBatis로 해결하였지만 실무에서는 JPA + QueryDSL을 사용한다 하여 QueryDSL에 관한 공부 하며 포스팅하려 합니다.
QueryDSL
- 쿼리를 자바 코드로 작성할 수 있도록 해줍니다.
- 자바 코드로 작성하기 때문에 개발자들도 쉽게 작성할 수 있고 문법 오류도 컴파일 시에 알 수가 있습니다.
- 데이터 조회 기능에 특화되어 있습니다.
QueryDSL 설정
- QueryDSL 플러그인 추가 및 버전 명시
buildscript {
ext {
queryDslVersion = "5.0.0"
}
}
plugins {
id 'org.springframework.boot' version '2.7.2'
id 'io.spring.dependency-management' version '1.0.12.RELEASE'
id 'java'
// querydsl 플러그인
id "com.ewerk.gradle.plugins.querydsl" version "1.0.10"
}
- QueryDSL 디펜던시 추가
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'
implementation 'mysql:mysql-connector-java'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
// QueryDSL 디펜던시
implementation "com.querydsl:querydsl-jpa:${queryDslVersion}" // 라이브러리
implementation "com.querydsl:querydsl-apt:${queryDslVersion}" // 코드 생성 기능
}
- QueryDSL 쿼리 타입 생성
// QueryDSL 사용 경로
def querydslDir = "$buildDir/generated/querydsl"
// JPA 사용여부 및 사용 경로 설정
querydsl {
jpa = true
querydslSourcesDir = querydslDir
}
// build시 사용할 soureSet 설정
sourceSets {
main.java.srcDir querydslDir
}
compileQuerydsl {
options.annotationProcessorPath = configurations.querydsl
}
// QueryDSL compileClassPath 상속
configurations {
compileOnly {
extendsFrom annotationProcessor
}
querydsl.extendsFrom compileClasspath
}
QClass 생성
- gradle > Tasks > other > compileQuerydsl 을 실행시켜 준다.
- build > generated > querydsl(gradle 설정) 이하에 생성된다.
- ex) 아래와 같은 Note Entity 클래스를 준비해 두고 QClass를 생성하면 QNote 클래스가 위 경로에 생성됩니다.
build > generated > querydsl > ... > QNote
/**
* QNote is a Querydsl query type for Note
*/
@Generated("com.querydsl.codegen.DefaultEntitySerializer")
public class QNote extends EntityPathBase<Note> {
private static final long serialVersionUID = 1632686613L;
public static final QNote note = new QNote("note");
public final StringPath content = createString("content");
public final NumberPath<Long> id = createNumber("id", Long.class);
public final StringPath title = createString("title");
public QNote(String variable) {
super(Note.class, forVariable(variable));
}
public QNote(Path<? extends Note> path) {
super(path.getType(), path.getMetadata());
}
public QNote(PathMetadata metadata) {
super(Note.class, metadata);
}
}
Querydsl 사용 설정
Querydsl를 사용할 수 있도록 JPAQueryFactory를 주입합니다.
QueryDslConfig
@Configuration
public class QueryDslConfig {
@PersistenceContext
private EntityManager entityManager;
@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(entityManager);
}
}
Repository 작성
Querydsl을 사용하기 위해 Spring Data Jpa Custom Repository를 만들어 적용할 것입니다.
기존 JpaRepository를 상속한 클래스와 Custom Repository를 같이 사용할 수 있게 됩니다.
NoteRepository
public interface NoteRepository extends JpaRepository<Note, Long>, NoteRepositoryCustom {
}
NoteRepositoryCustom
public interface NoteRepositoryCustom {
List<Note> findAllNotesByTitleContaining(String title);
}
NoteRepositoryImpl
import static com.example.querydsl.entity.QNote.note;
@RequiredArgsConstructor
@Repository
public class NoteRepositoryImpl implements NoteRepositoryCustom {
private final JPAQueryFactory jpaQueryFactory;
@Override
public List<Note> findAllNotesByTitleContaining(String title) {
return jpaQueryFactory.selectFrom(note)
.where(note.title.contains(title))
.fetch();
}
}
- 입력한 문자열이 제목에 포함되어 있는 메모들 찾기
- Custom 인터페이스를 구현하는 클래스 이름에 Impl에 붙여주어야 합니다.
Test
@DisplayName("QueryDsl 적용 확인")
@Test
public void queryDsl_Containing_Test() {
String title = "queryDsl 테스트 성공";
String content = "queryDsl 적용 성공입니다.";
noteRepository.save(new Note(title, content));
List<Note> result = noteRepository.findAllNotesByTitleContaining("성공");
assertThat(result.size()).isEqualTo(1);
assertThat(result.get(0).getTitle()).isEqualTo(title);
assertThat(result.get(0).getContent()).isEqualTo(content);
}
테스트가 성공했습니다.
Custom Repository 인터페이스를 만들고 이를 구현한 클래스를 만들어 Querydsl을 적용하였습니다.
인터페이스를 만들지 않고 바로 Querydsl Repository를 작성할 수도 있습니다. 이러한 방식은 구현이 비교적 간단해진다는 것과 Entity 제한이 없다는 것입니다. 이는 반대로 말하면 사용되는 Entity가 명시되지 않는다는 말과 같은 말이므로 특정 상황인 경우 고려해서 사용하는 것이 좋습니다.
QueryDsl 작동 방식과 설정하는 방법을 파악하느라 생각보다 시간이 오래 걸렸습니다,, 오늘은 설정까지만 하고 다음 포스팅에 더 다루도록 하겠습니다.
'Spring Data > Query DSL' 카테고리의 다른 글
QueryDSL Spring Data JPA + 동적 쿼리 (0) | 2023.01.18 |
---|---|
QueryDSL 벌크 쿼리 (0) | 2023.01.16 |
QueryDSL 동적 쿼리 (0) | 2023.01.16 |
QueryDSL DTO에 값 담기 (0) | 2023.01.15 |
QueryDSL 문법 (0) | 2023.01.15 |