Bibi's DevLog ๐Ÿค“๐ŸŽ

[JPA] QueryDSL ์‚ฌ์šฉํ•˜๊ธฐ ๋ณธ๋ฌธ

๐Ÿ–ฅ BE ๋ฐฑ์—”๋“œ/Spring ์Šคํ”„๋ง

[JPA] QueryDSL ์‚ฌ์šฉํ•˜๊ธฐ

๋น„๋น„ bibi 2021. 6. 26. 22:58

[210626]

[JPA] QueryDSL ์‚ฌ์šฉํ•˜๊ธฐ

  • JPA ์‚ฌ์šฉ ์‹œ, @Query์™€ JPQL ๋Œ€์‹  ๋ฉ”์„œ๋“œ๋กœ SQL๋ฌธ์„ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ

  • ๋ฌธ๋ฒ• ์˜ค๋ฅ˜๋ฅผ ์ปดํŒŒ์ผ ๋‹จ๊ณ„์—์„œ ํ™•์ธ ๊ฐ€๋Šฅ

build.gradle ์˜์กด์„ฑ ์ถ”๊ฐ€

plugins {
    id "com.ewerk.gradle.plugins.querydsl" version "1.0.10"
}

...

dependencies {
    implementation 'com.querydsl:querydsl-jpa'
    implementation 'com.querydsl:querydsl-apt' 
    // ์•„๋ž˜๋Š” ์—†๋‹ค๋ฉด ์ถ”๊ฐ€ (lombok์€ ์„ ํƒ)
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

..

def querydslDir = "$buildDir/generated/querydsl"
querydsl {
    jpa = true
    querydslSourcesDir = querydslDir
}
sourceSets {
    main.java.srcDir querydslDir
}
configurations {
    querydsl.extendsFrom compileClasspath
}
compileQuerydsl {
    options.annotationProcessorPath = configurations.querydsl
}

QueryDSL ํ”„๋กœ์ ํŠธ ์…‹ํŒ… (Qํด๋ž˜์Šค ์ƒ์„ฑ)

https://jojoldu.tistory.com/372 <- ์ด ๋งํฌ์—์„œ ์ดˆ๊ธฐ ์…‹ํŒ…์„ ๋”ฐ๋ผํ•˜๊ธฐ

Qํด๋ž˜์Šค ์ƒ์„ฑ ์•ˆ ๋  ๋•Œ ...

-> ํ˜น์‹œ Project Structure - Project Settings - Modules ์—์„œ Excluded Folders ์—์„œ build๊ฐ€ ๋นจ๊ฐ„์ƒ‰์œผ๋กœ ์„ค์ •๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.

์‚ฌ์šฉ ์˜ˆ - IssueRepositorySupport

๊ธฐ์กด IssueRepository ์ธํ„ฐํŽ˜์ด์Šค ๋ฉ”์†Œ๋“œ์— @Query ์ปค์Šคํ…€์ฟผ๋ฆฌ๋กœ ๊ตฌํ˜„ํ•˜๋˜ ๋ถ€๋ถ„์„ ๋ฉ”์†Œ๋“œ๋กœ ๋Œ€์ฒดํ•˜๋Š” ๋Š๋‚Œ. ํŽธํ•˜๋‹ค!

findByAssignee(), findByCommentUserId() : join๋ฌธ

@Repository
public class IssueRepositorySupport extends QuerydslRepositorySupport {

    private final JPAQueryFactory queryFactory;

    public IssueRepositorySupport(JPAQueryFactory queryFactory) {
        super(Issue.class);
        this.queryFactory = queryFactory;
    }


    public List<Issue> findByTitle(String title) {
        return queryFactory
                .selectFrom(issue)
                .where(issue.title.contains(title))
                .fetch();
    }

    public List<Issue> findByStatusTrue() {
        return queryFactory
                .selectFrom(issue)
                .where(issue.status.isTrue())
                .fetch();
    }

    public List<Issue> findByStatusFalse() {
        return queryFactory
                .selectFrom(issue)
                .where(issue.status.isFalse())
                .fetch();
    }

    public List<Issue> findByAuthor(Long userId) {
        return queryFactory
                .selectFrom(issue)
                .where(issue.user.id.eq(userId))
                .fetch();
    }

    public List<Issue> findByAssignee(Long userId) {
        return queryFactory
                .selectFrom(issue)
                .innerJoin(assignee)
                .on(assignee.issueId.eq(issue.id))
                .where(assignee.userId.eq(userId))
                .fetch();
    }

    public List<Issue> findByCommentUserId(Long userId) {
        return queryFactory
                .selectFrom(issue)
                .innerJoin(comment)
                .on(comment.issueId.eq(issue.id))
                .where(comment.user.id.eq(userId))
                .fetch();
    }

    public List<Issue> getFilteredIssues(IssueFilter issueFilter) {
        JPAQuery query = queryFactory
                .selectFrom(issue)
                .where(getPredicate(issueFilter));

        Long userId = issueFilter.getAssignee();
        if (userId != null) {
            query.innerJoin(assignee)
                    .on(assignee.issueId.eq(issue.id))
                    .where(assignee.userId.eq(userId));
        }

        userId = issueFilter.getCommenter();
        if (userId != null) {
            query.innerJoin(comment)
                    .on(comment.issueId.eq(issue.id))
                    .where(comment.user.id.eq(userId));
        }

        return query.fetch();

    }

    public Predicate getPredicate(IssueFilter filter) {
        return new PredicateBuilder(issue.isNotNull())
                .containsAnd(issue.title, filter.getTitle())
                .isBooleanAnd(issue.status, filter.getStatus())
                .isLongEqualAnd(issue.user.id, filter.getAuthor())
                .build();
    }
}

์œ„์˜ getPredicate()์— ํ•„์š”ํ•œ ํด๋ž˜์Šค์ธ PredicateBuilder

package com.codesquad.issuetracker.domain.issue;

import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.core.types.dsl.BooleanPath;
import com.querydsl.core.types.dsl.NumberPath;
import com.querydsl.core.types.dsl.StringPath;

public class PredicateBuilder {

    private final BooleanExpression predicate;

    public PredicateBuilder(BooleanExpression predicate) {
        this.predicate = predicate;
    }

    public PredicateBuilder containsAnd(StringPath qValue, String rValue) {
        if (rValue != null) {
            return new PredicateBuilder(predicate.and(qValue.containsIgnoreCase(rValue)));
        }
        return this;
    }

    public PredicateBuilder isBooleanAnd(BooleanPath qValue, Boolean rValue) {
        if (rValue != null) {
            return (rValue) ? new PredicateBuilder(predicate.and(qValue.isTrue()))
                    : new PredicateBuilder(predicate.and(qValue.isFalse()));
        }
        return this;
    }

    public PredicateBuilder isLongEqualAnd(NumberPath qValue, Long rValue) {
        if (rValue != null) {
            return  new PredicateBuilder(predicate.and(qValue.eq(rValue)));
        }
        return this;
    }

    public BooleanExpression build() {
        return predicate;
    }

}