ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Spring boot] Query DSL 동적 쿼리
    프로젝트/아카이뷰 2024. 1. 23. 08:52

    Query DSL 동적 쿼리 왜 궁금했을까❓

    Query DSL을 아카이뷰 프로젝트에 적용하는 과정을 보도록 하겠다.
     

    [Spring boot] Query DSL

    Query DSL 왜 궁금했을까❓ArchiVIEW의 검색 API를 만들어야 했는데 필터가 존재하여 Spring Data JPA를 사용하기에는 경우의 수를 고려하여 쿼리를 만들어야 했다. 상당히 비효율적이라 생각하여 동적 쿼

    pslog.co.kr

    위 포스팅을 통해 Query DSL에 대한 개념을 이해할 수 있다.

    Query DSL 실습

    Gradle 설정(Spring Boot 3.x)

    Query DSL은 JPA 표준이 아니기 때문에 별도로 라이브러리를 추가해야 한다.
    // QueryDSL
    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"
    • 프로젝트를 실행해보면 build/generated/annotationProcessor에 QClass가 생성된 것을 확인할 수 있다.

    • Query DSL을 사용하기 위해서는 EntityManager와 JPAQueryFactory를 Bean으로 등록해야 한다.
    @Configuration
    public class QueryDslConfig {
        @PersistenceContext
        private EntityManager entityManager;
    
        @Bean
        public JPAQueryFactory jpaQueryFactory() {
            return new JPAQueryFactory(entityManager);
        }
    }
    • QueryDSL은 JPAQueryFactory 객체를 이용하여 사용하는데 EntityManager를 파라미터로 받아 사용한다.
    • EntityManager는 여러 스레드가 동시에 접근하면 동시성 문제가 발생하여 Thread간에 공유가 되서는 안된다. 이를 해결하기 위해 @PersistenceContext를 통해 Thread-safe 상태를 유지한다.

    RecuritRepository

    public interface RecruitRepository extends JpaRepository<Recruit, Integer>, RecruitRepositoryCustom {
    }
    • Spring Data JPA에서 기본적으로 제공하는 기능과 QueryDSL의 Custom 기능들을 하나의 인터페이스에서 사용할 수 있도록 했다.

    RecuritRepositoryCustom

    public interface RecruitRepositoryCustom {
        List<Recruit> searchAll(RecruitDto.DetailListRequestDto requestDto);
    }
    

    RecuritRespositoryImpl

    @RequiredArgsConstructor
    public class RecruitRepositoryImpl implements RecruitRepositoryCustom {
        private final JPAQueryFactory factory;
        
        @Override
        public List<Recruit> searchAll(RecruitDto.DetailListRequestDto requestDto) {
            BooleanBuilder builder = new BooleanBuilder();
            QRecruit recruit = QRecruit.recruit;
    
            StringExpression start = Expressions.stringTemplate("FUNCTION('DATE_FORMAT', {0}, '%Y-%m')", recruit.start);
            StringExpression end = Expressions.stringTemplate("FUNCTION('DATE_FORMAT', {0}, '%Y-%m')", recruit.end);
    		
            // 회사 필터
            if(requestDto.getCompanyId() != 0) {
                builder.and(recruit.company.id.eq(requestDto.getCompanyId()));
            }
    
            return factory
                    .selectFrom(recruit)
                    .join(recruit.company).fetchJoin()
                    .where(builder
                            .and(start.eq(requestDto.getDate()).or(end.eq(requestDto.getDate()))))
                    .fetch();
        }
    }
    1. BooleanBuilder
      • 동적 쿼리를 생성하기 위해 사용하는 기능
    2. StringExpression
      • 채용 날짜 비교를 위한 변환
    3. selectFrom 
      • recurit 테이블로부터 조회
    4. join
      • inner join 수행
    5. fetchJoin
      • N + 1 문제를 개선하고 성능 향상을 위해 fetch join 수행
    6. where
      • BooleanBuilder와 StringExpression을 활용하여 조건문 설정
    7. fetch
      • Query를 생성하고 결과 반환

    '프로젝트 > 아카이뷰' 카테고리의 다른 글

    [Docker] Docker란?  (0) 2024.01.25
    [Spring boot] BooleanBuilder / BooleanExpression  (0) 2024.01.24
    [Spring boot] REST, REST API, RESTful  (0) 2024.01.22
    [Spring boot] JPA N + 1  (0) 2024.01.21
    [Spring boot] Query DSL  (0) 2024.01.20
Designed by Tistory.