본문 바로가기
컴퓨터 프로그래밍/Spring

[Spring] QueryDSL

by 한33 2024. 10. 3.

  • 타입 안전한 동적 쿼리를 직관적으로 작성할 수 있게 도와주는 Java 기반 쿼리 라이브러리

환경세팅

  • build.gradle
dependencies {
    // 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"
}
  • QueryDslConfig.java
@Configuration
class QueryDslConfig {

    @PersistenceContext
    private EntityManager em;

    @Bean
    public JPAQueryFactory jpaQueryFactory() {
        return new JPAQueryFactory(JPQLTemplates.DEFAULT, em);
    }
}

 

QueryDSL 설정 후 gradle build시, QClass가 생성됨

  • Entity 클래스의 메타 정보를 담고 있는 클래스
  • 타입 안정성(Type safe)을 보장하면서 쿼리를 작성할 수 있음
  • 컴파일 시점에 쿼리 오류를 확인할 수 없는 JPQL의 단점 보완

TodoRepository

 

TodoRepository 를 JpaRepository 와 TodoQueryRepository 를 다중상속 받게 함으로서, 기존의 JPA, JPQL 에 추가로 QueryDSL 을 사용할 수 있도록 하였다.


TodoQueryRepository

public interface TodoQueryRepository {
    Todo findByIdByDsl(long todoId);
}

TodoQueryRepositoryImpl

 

TodoQueryRepository 를 상속받는 구현체를 만들었다.

수동으로 @Repository 어노테이션을 달아서 지정해줘야하고 JPAQueryFactory 를 가져오고 생성자를 만들어준다.

@Repository
@RequiredArgsConstructor
public class TodoQueryRepositoryImpl implements TodoQueryRepository{

    private final JPAQueryFactory queryFactory;

    @Override
    public Todo findByIdByDsl(long todoId) {
        return queryFactory
                .select(todo)
                .from(todo)
                .join(todo.user, user).fetchJoin()
                .join(todo.comments, comments)
                .where(
                        todoIdEq(todoId)
                ).fetchOne();
    }

    private BooleanExpression todoIdEq(Long todoId) {
        return todoId != null ? todo.id.eq(todoId) : null;
    }
}

 

todoId 로 todo 를 조회하는 메서드

 

join(todo.user, user):

  • todo.user: Todo 엔티티와 연관된 User 엔티티를 지정함. 이는 @ManyToOne 관계로 설정되어 있습니다.
  • user: 조인할 User 엔티티의 별칭입니다. 이 별칭은 후속 쿼리에서 User에 접근하는 데 사용됨.
  • 간단하게 todo 와 user 를 연결한다고 생각하면 된다.

fetchJoin()

  • 지연 로딩(LAZY)을 무시하고 즉시 로딩(EAGER)처럼 연관된 User 데이터를 한 번에 가져오도록 강제함.
  • 즉, Todo 엔티티를 조회할 때 User 엔티티도 함께 조회됩니다.

 

todo 와 user, todo 와 comments 모두 일대다 관계이기 때문에 둘 다 fetchJoin() 을 걸 수는 없다.

만약 하나가 일대일 관계였으면 걸 수 있다.

 

그렇기 때문에 comments 는 직접 Todo Entity 로 들어가서 BatchSize 옵션을 넣어줘야한다.

 

Todo

@BatchSize(size = 10)
@OneToMany(mappedBy = "todo", cascade = CascadeType.REMOVE)
private List<Comment> comments = new ArrayList<>();