QueryDSL

2024. 10. 5. 18:10·Spring/QueryDSL, JPQL

객체 관계 매핑 프레임워크는 Enterprise Java의 핵심입니다. 이는 객체 지향 방식과 관계형 데이터베이스 모델 간의 불일치를 보완합니다. 또한 개발자가 더 깔끔하고 간결한 지속성 코드와 도메인 로직을 작성할 수 있도록 합니다. -QueryDSL 공식 홈페이지중 baeldung의 QueryDSL 목적

https://www.baeldung.com/intro-to-querydsl

 

즉 QueryDSL은 Java 언어로 작성된 쿼리를 위한 타입 안전 쿼리 생성 라이브러리입니다. SQL, JPA, MongoDB 등 다양한 데이터베이스와의 상호작용을 지원하며, 개발자가 보다 직관적이고 안전하게 데이터베이스 쿼리를 작성할 수 있도록 도와줍니다.

 

주요 특징

타입 안전성

 

QueryDSL은 쿼리의 문법과 데이터 타입을 컴파일 시점에 검사합니다. 이는 런타임 에러를 줄여주고, 코드 작성 시 IDE의 자동 완성 기능을 통해 쿼리를 더 빠르고 정확하게 작성할 수 있도록 합니다.

 

유연한 백엔드 지원

 

QueryDSL은 SQL, JPA, MongoDB, Lucene 등 다양한 데이터베이스를 지원합니다. 이를 통해 개발자는 특정 기술에 종속되지 않고, 여러 데이터베이스와의 상호작용을 동일한 방식으로 처리할 수 있습니다.

 

가독성 높은 쿼리

 

QueryDSL은 SQL 쿼리를 Java 코드로 변환할 수 있도록 설계되었습니다. 이는 쿼리의 가독성을 높이고, 쿼리를 동적으로 생성할 수 있는 유연성을 제공합니다.

 

정적 쿼리 생성

 

쿼리를 정적으로 생성하므로, 쿼리의 구조를 쉽게 이해하고 수정할 수 있습니다. 이는 팀원 간의 협업을 용이하게 하고, 코드 유지 관리성을 높여줍니다.

 

 

적용 방법 

Ex) Gradle

(QueryDSL에 사용할 JPA 엔티티 클래스가 이미 적용된 기준)

(필자는 Todo와 User의 엔티티 기준으로 작성할 예정)

1. QueryDSL 의존성

// QueryDSL
    implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
    annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta"
    annotationProcessor "jakarta.annotation:jakarta.annotation-api"
    annotationProcessor "jakarta.persistence:jakarta.persistence-api"

implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'

 의존성 설명

  • com.querydsl:querydsl-jpa는 QueryDSL의 JPA(자바 지속성 API) 모듈로, JPA를 사용하여 데이터베이스와 상호작용하는 기능을 제공합니다.
  • 버전은 5.0.0이며, jakarta는 Jakarta EE를 사용하고 있음을 나타냅니다. 이 버전은 Jakarta EE 9 및 그 이후 버전에서 사용되는 새로운 패키지 구조를 따릅니다.

역할

  • JPA 엔티티를 기반으로 쿼리를 생성하고 실행하는 데 필요한 기능을 제공합니다. QueryDSL은 SQL을 Java 코드로 안전하게 작성할 수 있게 해 줍니다.

annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta"

의존성 설명

  • com.querydsl:querydsl-apt는 QueryDSL의 APT(Annotation Processing Tool) 모듈입니다. JPA 엔티티에 대한 메타데이터를 생성하기 위해 애노테이션 프로세서를 제공합니다.

역할

이 모듈은 JPA 엔티티 클래스에서 Q 클래스(쿼리 DSL을 위한 클래스)를 자동으로 생성합니다. Q 클래스는 QueryDSL의 쿼리를 구성하는 데 필요한 타입 안전한 표현을 제공합니다.

 

annotationProcessor "jakarta.annotation:jakarta.annotation-api"

의존성 설명

  • jakarta.annotation:jakarta.annotation-api는 Jakarta EE의 애노테이션 API입니다.
  • 이는 Java EE와 Jakarta EE에서 사용하는 표준 애노테이션을 포함합니다.

역할

QueryDSL의 APT 모듈에서 필요로 하는 애노테이션을 제공하여, JPA 엔티티에 대한 메타데이터를 생성할 수 있도록 돕습니다.

 

annotationProcessor "jakarta.persistence:jakarta.persistence-api"

의존성 설명

jakarta.persistence:jakarta.persistence-api는 Jakarta EE의 JPA API입니다.

역할

JPA 엔티티 클래스의 메타데이터를 처리하는 데 필요합니다. 이 API는 데이터베이스와의 상호작용을 위한 기본 인터페이스를 제공합니다.

 

필자는 해당 JPQL을 QueryDSL로 변환하는 작업하면서 QueryDSL을 작성해볼려고 한다.

2. JPAConfiguration 

QueryDSL을 사용하기 위한 설정 클래스입니다.

 

JPAConfiguration이 왜 필요한가?

EntityManager 주입

  • @PersistenceContext 어노테이션을 사용하여 EntityManager를 주입합니다. EntityManager는 JPA에서 데이터베이스와 상호작용하는 데 필요한 객체로, 데이터베이스의 CRUD(Create, Read, Update, Delete) 작업을 수행하는 데 필수적입니다.
  • QueryDSL은 JPA를 기반으로 동작하기 때문에, EntityManager가 필요합니다.

- @PersistenceContext : JPA에서 엔티티 객체의 생명 주기를 관리하는 환경을 의미. 영속성 컨텍스트 내에서 엔티티 객체는 데이터베이스와 동기화되며, 엔티티의 상태 변경이 데이터베이스에 반영된다.

 

JPAQueryFactory 빈 등록

  • @Bean 어노테이션을 통해 JPAQueryFactory 객체를 Spring의 Application Context에 빈으로 등록합니다.
  • JPAQueryFactory는 QueryDSL에서 쿼리를 생성하는 데 사용되는 주체로, 이 객체를 통해 타입 안전한 쿼리를 작성할 수 있습니다.

- @Bean : 애플리케이션의 특정 구성 및 객체 생성을 중앙 집중화하여 관리하기 위해 사용됩니다. 이를 통해 개발자는 Spring 컨테이너가 객체의 생명 주기를 관리하도록 하고, 객체 간의 의존성을 주입할 수 있습니다.

 

의존성 주입

  • Spring의 IoC(제어의 역전) 원칙을 따르기 때문에, JPAQueryFactory를 사용하는 클래스에서는 DI(의존성 주입)를 통해 JPAQueryFactory 인스턴스를 쉽게 사용할 수 있습니다.
  • 이렇게 하면 코드의 재사용성이 높아지고, 관리가 용이해집니다.

구성의 분리

  • 이 설정 클래스를 통해 애플리케이션의 설정과 비즈니스 로직을 분리할 수 있습니다.
  • 설정 관련 코드와 비즈니스 로직을 분리하면 코드의 가독성이 높아지고, 유지보수가 용이해집니다.

QueryDSL의 초기화

  • JPAQueryFactory는 내부적으로 EntityManager를 사용하여 쿼리를 실행하므로, 이 클래스가 있어야 QueryDSL이 제대로 작동합니다.
  • QueryDSL은 이 객체를 통해 쿼리를 빌드하고 실행할 수 있으며, EntityManager와 통합되어 SQL 쿼리로 변환됩니다.

JPAConfiguration 클래스는 QueryDSL을 사용하기 위해 필수적인 설정 클래스로, EntityManager를 주입받아 JPAQueryFactory를 빈으로 등록하려 쿼리 생성을 가능하게 합니다. -> 이러한 설정이 있어야 QueryDSL을 통해 데이터베이스와 안전하고 효율적으로 상호작용할 수 있다.

 

3. RepositoryCustom

RepositoryCustom 인터페이스는 QueryDSL을 사용하여 데이터베이스와 상호작용하는 데 매우 중요한 역할을 합니다.

RepositoryCustom이 왜 필요한가?

커스텀 쿼리 작성

  • QueryDSL은 동적 쿼리를 안전하게 작성할 수 있도록 도와줍니다. 기본 JPA 메서드로는 처리하기 어려운 복잡한 쿼리를 만들 때 이 인터페이스가 필요합니다.
  • 예를 들어, findByIdWithUser(Long todoId) 메서드는 특정 todoId에 대한 Todo 엔티티와 해당 사용자 정보를 함께 조회하는 쿼리를 정의하는 데 사용됩니다. QueryDSL의 JPAQueryFactory를 통해 동적 쿼리를 생성할 수 있습니다.

비즈니스 로직과 데이터 접근 분리

  • QueryDSL을 사용하면 비즈니스 로직과 데이터 접근 로직을 명확하게 분리할 수 있습니다.
  • TodoRepositoryCustom 인터페이스를 구현하여 데이터 접근 로직을 집중화하면 코드의 가독성과 유지보수성이 높아집니다.

타입 안전성

  • QueryDSL은 타입 안전성을 제공합니다. 즉, 쿼리를 컴파일 시점에 검사하여 오류를 방지할 수 있습니다.
  • TodoRepositoryCustom을 통해 QueryDSL을 구현하면 이러한 타입 안전성을 활용하여 안정적인 데이터 접근을 구현할 수 있습니다.

다양한 쿼리 지원

  • QueryDSL은 다양한 쿼리 방식을 지원하며, 이를 통해 복잡한 조인이나 필터링을 쉽게 구현할 수 있습니다.
  • TodoRepositoryCustom 인터페이스는 QueryDSL 메서드와 논리를 추가하는 기본 틀을 제공하여, 다양한 쿼리 요구사항을 처리할 수 있게 합니다.

재사용성 및 확장성

  • 여러 엔티티에 대한 커스텀 메서드를 정의할 수 있으며, 향후 요구사항이 변경되거나 확장될 때, TodoRepositoryCustom 인터페이스를 통해 쉽게 추가할 수 있습니다.
  • 이렇게 하면 QueryDSL의 장점을 살리면서도 구조적으로 잘 정리된 코드를 유지할 수 있습니다.

TodoRepositoryCustom 인터페이스는 QueryDSl을 효과적으로 활용하기 위하 기반을 제공합니다. 그래서 이를 통해 복잡한 쿼리를 작성이 가능하고 비즈니스 로직과 데이터 접근 로직을 분리하며 타입 안전성을 확보하고, 코드의 재사용성과 확장성을 높일 수 있습니다!

 

4. TodoRepositoryImpl

TodoRepositoryImpl 클래스는 TodoRepositoryCustom 인터페이스를 구현하여 사용자 요구사항에 맞게 커스텀 쿼리 메서드를 제공합니다. 

 

QueryDSL 활용

  • 이 클래스는 QueryDSL의 JPAQueryFactory를 사용하여 쿼리를 작성합니다. JPAQueryFactory는 JPA 엔티티를 대상으로 쿼리를 작성하는 데 유용한 도구입니다.
  • private final JPAQueryFactory jqf;를 통해 JPAQueryFactory의 인스턴스를 주입받아 쿼리 작성 시 사용할 수 있습니다.

QueryDSL의 타입 안전성 활용

  • QueryDSL은 컴파일 타임에 쿼리의 유효성을 검사할 수 있는 기능을 제공합니다. QTodo와 QUser와 같은 Q타입 클래스를 통해 쿼리의 속성을 직접 접근할 수 있으며, 이는 코드의 안정성을 높이고 실수를 줄이는 데 기여합니다. TodoRepositoryImpl 클래스는 이러한 Q타입 클래스를 사용하여 쿼리를 작성합니다.

Fetch Join을 통한 N+1 문제 해결

  • .leftJoin(todo.user, user)와 .fetchJoin()을 통해 Todo와 연관된 User 엔티티를 동시에 조회합니다.
  • 이는 N+1 문제를 방지하는 데 중요한 역할을 합니다.

- N+1 문제란? : 하나의 쿼리로 데이터를 조회한 후 각 데이터에 대해 추가 쿼리가 실행되는 문제를 말합니다. 이로 인해 성능 저하가 발생할 수 있습니다.

 

타입 안전성

  • QueryDSL을 사용하여 생성된 QTodo와 QUser 객체는 타입 안전성을 제공합니다.
  • 즉, 쿼리 작성 시 컴파일 타임에 타입 오류를 체크할 수 있어 런타임 오류를 줄일 수 있습니다.

 

Optional을 통한 Null 안전성

  • fetchOne() 메서드는 단일 결과를 반환하며, 결과가 없을 경우 null을 반환합니다.
  • Optional.ofNullable(result)를 사용하여 결과를 Optional로 감싸 null 값을 안전하게 처리합니다. 이를 통해 클라이언트 코드에서 null 체크를 쉽게 할 수 있습니다.

 

전체적인 흐름

 

1. 쿼리 생성: QTodo와 QUser를 통해 쿼리를 구성합니다.

2. 조인 수행: Todo 엔티티와 연관된 User 정보를 left join합니다.

3. fetchJoin() 사용: N+1 문제를 해결하기 위해 fetch join을 수행합니다.

4. 조건 지정: todo.id.eq(id)로 조건을 추가하여 특정 todoId에 해당하는 Todo를 찾습니다.

5. 결과 반환: 조회된 Todo 객체를 Optional로 감싸서 반환합니다.

 

즉 TodoRepositoryImpl 클래스는 QueryDSL을 사용하여 복잡한 쿼리를 간편하고 안전하게 작성이 가능합니다. 그리고 커스텀 메서드를 통해 비즈니스 로직에 맞는 쿼리를 효율적으로 처리가 가능합니다.

반응형

'Spring > QueryDSL, JPQL' 카테고리의 다른 글

QueryDSL을 이용한 검색 기능  (0) 2024.10.07
JPQL  (4) 2024.10.02
'Spring/QueryDSL, JPQL' 카테고리의 다른 글
  • QueryDSL을 이용한 검색 기능
  • JPQL
wanduek
wanduek
차근차근 쌓아가는 지식
  • wanduek
    완득님의 블로그
    wanduek
  • 전체
    오늘
    어제
    • 분류 전체보기 (194)
      • Onboarding (0)
      • 아티클 (0)
      • SQL 정리 (20)
      • 웹개발 기초정리(프엔) (9)
      • (Java)코테연습 (27)
      • 미니 프로젝트 (9)
        • 자기소개 웹페이지 (5)
        • 수강생 관리 프로그램(Java) (4)
      • GitHub 오류 및 명령어 정리 (7)
      • Java 문법정리 (Array부터) (17)
      • Java로 이용한 기능 (2)
      • Web지식 (12)
        • web3 (6)
        • 네트워크 (6)
      • 인텔리제이 오류 대처 방법 (1)
      • Computer Science (7)
      • Spring (38)
        • 미니 프로젝트(배달) (2)
        • Spring 트러블 슈팅 (3)
        • QueryDSL, JPQL (3)
        • 미니 프로젝트(뉴스피드) (5)
        • 미니 프로젝트(Trello) (2)
      • 개발자 밋업 (1)
      • Docker (2)
      • Redis (1)
      • AWS (6)
        • AWS 트러블 슈팅 (2)
      • Jmeter (2)
      • 우리 지금 만나 (12)
      • Kafka (1)
      • RabbitMQ (2)
        • 트러블 슈팅 (1)
      • STOMP (2)
      • WebSocket (3)
        • 트러블 슈팅 (2)
      • Node.js (2)
      • Elastic Search (2)
      • Nginx (1)
      • 퍼블엘 (2)
      • openresty (3)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    ㅏㄷ
    github
    백앤드개발자 #초심자
    jquery
    where
    git
    json
    SQL
    groupby
    fetch
  • 최근 댓글

  • 최근 글

  • 반응형
  • hELLO· Designed By정상우.v4.10.0
wanduek
QueryDSL
상단으로

티스토리툴바