-
[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(); } }
- BooleanBuilder
- 동적 쿼리를 생성하기 위해 사용하는 기능
- StringExpression
- 채용 날짜 비교를 위한 변환
- selectFrom
- recurit 테이블로부터 조회
- join
- inner join 수행
- fetchJoin
- N + 1 문제를 개선하고 성능 향상을 위해 fetch join 수행
- where
- BooleanBuilder와 StringExpression을 활용하여 조건문 설정
- 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