JPA 심화 2-3

5w31892p·2023년 2월 3일
0

JPA 심화

목록 보기
8/19

SpringData 쿼리와 QueryDSL

:: SpringData 쿼리

  • SprintData Common 의 CRUDRepository + PagingAndSortingRepository 이 쿼리기능을 제공

사용 방법

  • 프로그래밍되어 제공되는 쿼리명 규칙
returnType {접두어}{도입부}By{프로퍼티 표현식}(조건식)[(And | Or){프로퍼티 표현식}(조건식)](OrderBy{프로퍼티}Asc | Desc) (매개변수...)
-표현식
접두어Find, Get, Query, Count, ...
도입부Distinct, First(N), Top(N)
프로퍼티 표현식Person.Address.ZipCode => find(Person)ByAddress_ZipCode(...)
조건식IgnoreCase, Between, LessThan, GreaterThan, Like, Contains, ...
정렬 조건`OrderBy{프로퍼티}Asc
리턴 타입E, Optional<E>, List<E>, Page<E>, Slice<E>, Stream<E>
매개변수Pageable, Sort
  • 예제
// 기본
List<User> findByNameAndPassword(String name, String password);

// distinct (중복제거)
List<User> findDistinctUserByNameOrPassword(String name, String password);
List<User> findUserDistinctByNameOrPassword(String name, String password);

// ignoring case (대소문자 무시)
List<User> findByNameIgnoreCase(String name);
List<User> findByNameAndPasswordAllIgnoreCase(String name, String password);

// 정렬
List<Person> findByNameOrderByNameAsc(String name);
List<Person> findByNameOrderByNameDesc(String name);

// 페이징
Page<User> findByName(String name, Pageable pageable);  // Page 는 카운트쿼리 수행됨
Slice<User> findByName(String name, Pageable pageable); // Slice 는 카운트쿼리 수행안됨
List<User> findByName(String name, Sort sort);
List<User> findByName(String name, Pageable pageable);

// 스트림 (stream 다쓴후 자원 해제 해줘야하므로 try with resource 사용추천)
Stream<User> readAllByNameNotNull();

:: QueryDSL

  • QueryDSL의 Predicate 인터페이스로 조건문을 여러개를 구성하여 따로 관리 가능
    • findOne(Predicate), findAll(Predicate) 주로 이 2개 메소드 사용
      • findOne = Optional<T> 리턴
      • findAll = List<T> | Page<T> | Iterable<T> | Slice<T> 리턴
  • Type Safe 기능
    • 조건문 구성시에 사용되는 객체, 필드 조건이 실제 타입과 일치한지 체크
  • QueryDSL 의존성을 추가하면 SpringData에 의해 QueryDslPredicateExecutor 인터페이스가 추가된다.
    • QueryDslPredicateExecutor 는 Repository가 QueryDsl 을 실행할 수 있는 인터페이스를 제공하는 역할을 한다.

장점

  1. 문자가 아닌 코드로 쿼리를 작성함 -> 컴파일 시점에 문법 오류를 쉽게 확인 가능
  2. 자동 완성 등 IDE의 도움을 받을 수 있음
  3. 동적인 쿼리 작성이 편리
  4. 쿼리 작성 시 제약 조건 등을 메서드 추출을 통해 재사용 가능

Spring 3.X 버전

  • 의존성만 추가하면 빌드에 자동으로 포함되서 실행
// QueryDSL 적용을 위한 의존성 (SpringBoot3.0 부터는 jakarta 사용해야함)
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta"
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"

Spring 2.X 버전

  • QueryDSL 빌드 Task 를 따로 설정
  • queryDSL 의존성, 소스 디렉토리 설정, annotation 프로세서 설정, Task 정의
plugins {
    ...
    id "com.ewerk.gradle.plugins.querydsl" version "1.0.10"
}

compileQuerydsl{
    options.annotationProcessorPath = configurations.querydsl
}

configurations {
    ...
    querydsl.extendsFrom compileClasspath
}

def querydslSrcDir = 'src/querydsl/generated'

querydsl {
    library = "com.querydsl:querydsl-apt"
    jpa = true
    querydslSourcesDir = querydslSrcDir
}

sourceSets {
    main {
        java {
            srcDirs = ['src/main/java', querydslSrcDir]
        }
    }
}

project.afterEvaluate {
    project.tasks.compileQuerydsl.options.compilerArgs = [
            "-proc:only",
            "-processor", project.querydsl.processors() +
                    ',lombok.launch.AnnotationProcessorHider$AnnotationProcessor'
    ]
}

dependencies {
    implementation("com.querydsl:querydsl-jpa") // querydsl
    implementation("com.querydsl:querydsl-apt") // querydsl
    ...
}

:: QueryDSL gradle 빌드 스크립트 원리

  • Gradle 빌드시에 QueryDSL은 프로젝트 내의 @Entity 어노테이션을 선언한 클래스를 탐색하고,  JPAAnnotationProcessor를 사용해 Q클래스를 생성한다.
  • querydsl-apt@Entity@Id 등의 매핑정보 Annotation을 알 수 있도록, javax.persistence과 javax.annotation을 annotationProcessor에 함께 추가한다.
    • annotationProcessor는 Java 컴파일러 플러그인으로, 컴파일 단계에서 어노테이션을 분석 및 처리함으로써 추가적인 파일을 생성한다.
  • 개발 환경에서 생성된 Q클래스를 인지할 수 있도록 /build/generated 디렉토리를 프로젝트의 sourceSet에 추가
    • 이렇게 되면 IDE 에서도 생성된 Q클래스 파일을 인식하여 개발할 수 있게 된다.

QueryDSL 실습을 위해서는 Repository에 QuerydslPredicateExecutor<Channel> 의존성 추가 해야 한다.

:: QuerydslPredicateExecutor 활용

  • QuerydslPredicateExecutor 로는 Join 연산이 불가능하여 구현 불가능 -> 단점
    • 멤버 컬렉션까지만 조회 가능하며 이것을 묵시적 조인(1 Depth 자동 조인) 이라고 한다.
      • 즉, channel 의 threads 까지만 접근 가능한것이 묵시적 조인
    • 반면에, Join 연산이 수행되는건 명시적 조인 이라고 한다. (2 Depth 이상 조인)
  • Join 없이 조건이 많이 추가될수록 QuerydslPredicateExecutor 활용 가능하다.
    • 실무에서는 Join이 많이 발생하기 때문에 JPAQueryFactory 가 주로 쓰임
  • Join이 필요하다면 JPQL 과 JPAQueryFactory 로 하면 됨

GitHub 실습 코드

0개의 댓글