QueryDSL 이게 뭘까

nGyu·2022년 6월 5일
1
post-thumbnail

요새 코드를 좀 둘러보다가 보면 한번 씩 이런 형태의 코드를 본 적이 있었다.

JPAQeuryFactory.selectFrom(post)
				.where(post.id.eq(user.id))
				.orderBy(post.created_at);

JPA도 편하지만, 조금 구체적으로 조회를 할 때 더욱 효율적일것 같다는 생각에 자료를 조금 찾아보기 시작했다.

QueryDSL

스프링에서 SQL문법과 비슷하게 작성을 하기 위해서 어떻게 해야 하는지에 대해 검색을 좀 해보았는데, Native Query를 작성하는 Query 어노테이션이 자주 나오게 되었는데 이는 JPA에서 권장하는 내용은 아니라고 들어 이와 관련된 내용들은 깊게 찾아보지 않았다.

그렇게 찾게된 방법이 바로 QueryDSL 이었다.

사용하는 이유

QueryDSL을 사용함에 있어 이것이 무엇인지, 왜 사용하는지에 대해 찾아보게 되었다.

  1. 쿼리를 자바코드로 작성할 수 있다.
  2. 복잡한 쿼리 혹은 동적 쿼리를 해결할 수 있다.
  3. 자바 코드로 작성하기 때문에 문법 오류를 컴파일 시점에 잡아낼 수 있다

이렇게 총 3가지의 장점을 언급해 주었다.

사용 방법

build.gradle에서 간단한 설정만 해주면 된다.

plugins{
	id 'com.ewerk.gradle.plugins.querydsl' version '1.0.10'
  id 'java'
}

...

implementation 'com.querydsl:querydsl-jpa'
implementation 'com.querydsl:querydsl-apt'

plugin에 위 코드를 추가하고, 아래 두 개의 패키지를 implementation 해준다.
그리고 맨 아래쪽에 아래 코드를 추가해주자.

tasks.named('test') {
    useJUnitPlatform()
}

//querydsl 추가
//def querydslDir = 'src/main/generated'
def querydslDir = "$buildDir/generated/querydsl"
querydsl {
    library = "com.querydsl:querydsl-apt"
    jpa = true
    querydslSourcesDir = querydslDir
}
sourceSets {
    main {
        java {
            srcDirs = ['src/main/java', querydslDir]
        }
    }
}
compileQuerydsl{
    options.annotationProcessorPath = configurations.querydsl
}
configurations {
    querydsl.extendsFrom compileClasspath
}

이 코드를 작성하고 gradle을 적용해주고 compileQuerydsl을 실행시켜준다.

Inteliij를 사용하고 있다면, compileQuerydsl 좌측 코드라인에 보면 초록색 실행버튼이 있을것이다. 해당 버튼을 눌려주면 compileQuerydsl이 실행 되며 build 폴더 안에 generated 폴더가 생기고, 여태 만든 entity들이 Q라는 키워드가 붙은 상태로 추가가 되어 있을것이다.

이 외에도 IDE 우측을 보면 Gradle 탭을 클릭 후 [Project] → Tasks → other 경로로 들어오면 아래와 같이 compileQuerydsl이 있을텐데 이를 더블클릭 해주면 된다.

코드에 적용시켜보기

기존 Repository가 저장된 폴더에 새로운 클래스 파일을 만들고 아래와 같이 작성을 한다.

@Repository
public class PostCustomRepositoryImpl implements PostCustomRepository {
    private final long NEW_POST_LIMIT_COUNT = 5L;

    private final JPAQueryFactory jpaQueryFactory;

    public PostCustomRepositoryImpl(JPAQueryFactory jpaQueryFactory) {
        this.jpaQueryFactory = jpaQueryFactory;
    }

    @Override
    public List<PostEntity> findFiveNewPosts() {
        return jpaQueryFactory
                .select(postEntity)
                .from(postEntity)  
								.limit(NEW_POST_LIMIT_COUNT);
    }
}

여기서 보면 PostCustomRepository라는것을 상속받고 있는데 이는 새로운 인터페이스 이다.

public interface PostCustomRepository {
    List<PostEntity> findFiveNewPosts();
}

그리고 이렇게 새로 만들어진 interface는 기존 Repository에 상속시켜준다.

public interface PostRepository extends JpaRepository<PostEntity, String>, PostCustomRepository{
}

다중 상속이 되기 때문에 이렇게 할 수 있는것이다.
이렇게 하면 Service단에서 PostRepository만을 불러와도 다형성 때문에 findFiveNewPosts() 메서드를 사용할 수 있게 된다.

즉,

@Service
public class PostService {

    @Autowired
    private PostRepository postRepository;

    public List<PostEntity> getNewFivePost() {
			return postRepository.findFiveNewPosts();
    }
}

JPAQueryFactory

그런데 이상하다 중간에 JPAQueryFactory라는것이 있다. 이것은 무엇일까

이는, JPQL 쿼리를 위한 JPAQuery의 Factory 패턴을 적용한 것이다.

결국, JPAQuery를 사용한다는 사실은 변함이 없다.
그저 JPAQuery 를 Factory 패턴을 적용했다는 것인데, 이는 다른 글에서 이야기를 해보자.

profile
지금보다 내일을, 모레를 준비하자

0개의 댓글