Everyday Dev System

QueryDsl 기초 본문

내배캠 주요 학습/JPA 심화

QueryDsl 기초

chaeyoung- 2023. 8. 1. 11:52

참조 : 내배캠 JPA 심화 강의 자료

https://teamsparta.notion.site/SpringData-QueryDSL-85b57e18a5204e70b6035a319d63f866

 

 

 

 

오류 시 해결

1. QChannel 이 활성화가 안될 경우 main 메서드가 있는 클래스를 다시 실행.
2. 테스트 코드시 Repository의 Bean 생성이 안될 경우에는
1) main 메서드가 있는 클래스에 아래 어노테이션 기재 여부 확인
     @SpringBootApplication
     @EnableJpaRepositories
2) 그럼에도 안 될 경우에는 테스트 코드에 @SpringBootTest 어노테이션이 명시되어있는지 확인
3) 그래도 안된다면, Repository 클래스에 상속한 클래( JpaRepository, QuerydslPredicateExecutor ) 의
     <> 안의 파라미터 클래스가 Entity로 매핑이 잘 되는지 확인.
4) 그럼에도 안된다면, 재빌드 혹은 재부팅 혹은 IDEA 껐다가 다시 켜기

 

 

 

QueryDSl을 Spring boot 3.0 버전부터는 의존성만 추가하면 사용이 가능해졌다.

 

 

사용 방법

위치 Query명
접두어 Find, Get, Query, Count, ...
도입부 Distinct, First(N), Top(N)
프로퍼티 표현식 Person.Address.ZipCode => find(Person)ByAddress_ZipCode(...)
조건식 IgnoreCase, Between, LessThan, GreaterThan, Like, Contains, ...
정렬 조건 OrderBy{프로퍼티}Asc|Desc
리턴 타입 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();

 

 

원리

의존성을 추가하면 Spring Data 에 의해 QueryDslPredicateExecuter 인터페이스가 추가가 된다.

이는 JpaRepository가 QueryDsl이 실행이 가능하도록 인터페이스를 제공한다.

 

 

 

기능

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

 

 

장점

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

 

 

 

의존성 추가 후 재빌드 및 main 실행

아래 의존성을 build.gradle 에 추가를 한다.

    // 9. 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"

 

 

 

 

 

 

예시 코드

ChannelRepository 에서 QueryDsl을 사용한다고 치면, 아래와 같이 

QuerydslPredicateExecutor<Channel> 을 상속받아야 한다.

package me.chaeyoung.jpa.channel;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;

public interface ChannelRepository extends JpaRepository<Channel, Long>,
    QuerydslPredicateExecutor<Channel> {

}

 

ChannelRepository 의 다이어그램을 살펴보면 아래과 같다.

JpaRepository 와 QuerydslPredicateExecutor를 상속받았다.

 

 

테스트 코드를 실행해보자.

Channel 테이블에 name 칼럼값이 "chaeyoung"인 레코드를 insert 해보았다.

그 후, name 칼럼값이 "CHAEYOUNG"와 일치한 레코드를 조회하는 테스트이다.

package me.chaeyoung.jpa.channel;

import com.querydsl.core.types.Predicate;
import java.util.Optional;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Rollback;
import org.springframework.transaction.annotation.Transactional;

@SpringBootTest
@Transactional
@Rollback(value = false)
class ChannelRepositoryTest {

  @Autowired
  private ChannelRepository channelRepository;

  @Test
  void queryDslTest() {
    // given
    var newChannel = Channel.builder().name("chaeyoung").build();
    channelRepository.save(newChannel);

    Predicate predicate = QChannel.channel
        .name.equalsIgnoreCase("CHAEYOUNG");

    // when
    Optional<Channel> optional = channelRepository.findOne(predicate);

    // then
    assert optional.get().getName().equals(newChannel.getName());
  }

}

 

 

QueryDsl을 잘 활용하여 테스트가 성공했다.

 

'내배캠 주요 학습 > JPA 심화' 카테고리의 다른 글

Auditing 활용하기  (1) 2023.08.01
QueryDSL 활용 코드  (0) 2023.08.01
Repository 에 기능 추가하기  (0) 2023.07.31
JpaRepository 기능 제한하기  (0) 2023.07.31
Cascade.REMOVE 와 orphanRemoval=true 차이  (0) 2023.07.31