Skip to content

Commit eb93ea8

Browse files
committed
refactor: querydsl 쿼리 최적화
1 parent 186633d commit eb93ea8

18 files changed

Lines changed: 515 additions & 357 deletions

File tree

build.gradle

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ configurations {
1919
}
2020
}
2121

22+
sourceSets {
23+
main {
24+
java {
25+
srcDirs += 'build/generated/sources/annotationProcessor/java/main'
26+
}
27+
}
28+
}
29+
2230
repositories {
2331
mavenCentral()
2432
}
@@ -29,16 +37,32 @@ dependencies {
2937
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
3038
implementation 'org.springframework.boot:spring-boot-starter-validation'
3139
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
40+
41+
// AWS SES
3242
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
3343
implementation 'com.amazonaws:aws-java-sdk-ses:1.12.472'
44+
45+
// Use Parse
3446
implementation 'org.apache.httpcomponents.client5:httpclient5:5.4.3'
3547
implementation 'org.seleniumhq.selenium:selenium-java:4.31.0'
3648
implementation 'org.jsoup:jsoup:1.18.3'
3749
implementation 'com.rometools:rome:2.1.0'
50+
51+
// Spring Security
3852
implementation 'org.springframework.boot:spring-boot-starter-security'
3953
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
54+
55+
// 기본 내장 tomcat 제외
56+
implementation('org.springframework.boot:spring-boot-starter-web') {
57+
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'
58+
}
59+
implementation 'org.springframework.boot:spring-boot-starter-undertow'
60+
61+
// OpenFeign Fork QueryDsl
4062
implementation 'io.github.openfeign.querydsl:querydsl-jpa:6.11'
41-
implementation 'io.github.openfeign.querydsl:querydsl-apt:6.11'
63+
annotationProcessor "io.github.openfeign.querydsl:querydsl-apt:6.11:jpa"
64+
annotationProcessor "jakarta.persistence:jakarta.persistence-api"
65+
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
4266

4367
testImplementation 'org.springframework.security:spring-security-test'
4468
testImplementation 'org.springframework.boot:spring-boot-starter-test'
@@ -51,12 +75,6 @@ dependencies {
5175
runtimeOnly 'com.mysql:mysql-connector-j'
5276
runtimeOnly 'com.h2database:h2'
5377
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
54-
55-
implementation('org.springframework.boot:spring-boot-starter-web') {
56-
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'
57-
}
58-
implementation 'org.springframework.boot:spring-boot-starter-undertow'
59-
6078
}
6179

6280
tasks.named('test') {
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.davcatch.devcatch.common.config;
2+
3+
import org.springframework.context.annotation.Bean;
4+
import org.springframework.context.annotation.Configuration;
5+
6+
import com.querydsl.jpa.impl.JPAQueryFactory;
7+
8+
import jakarta.persistence.EntityManager;
9+
import jakarta.persistence.PersistenceContext;
10+
11+
@Configuration
12+
public class QueryDslConfig {
13+
14+
@PersistenceContext
15+
private EntityManager entityManager;
16+
17+
@Bean
18+
public JPAQueryFactory jpaQueryFactory() {
19+
return new JPAQueryFactory(entityManager);
20+
}
21+
}
Lines changed: 1 addition & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,17 @@
11
package com.davcatch.devcatch.repository.article;
22

3-
import java.util.List;
4-
import java.util.Optional;
53
import java.util.Set;
64

7-
import org.springframework.data.domain.Page;
8-
import org.springframework.data.domain.Pageable;
9-
import org.springframework.data.jpa.repository.EntityGraph;
105
import org.springframework.data.jpa.repository.JpaRepository;
116
import org.springframework.data.jpa.repository.Query;
127

138
import com.davcatch.devcatch.domain.article.Article;
14-
import com.davcatch.devcatch.domain.tag.TagType;
159

16-
public interface ArticleRepository extends JpaRepository<Article, Long> {
17-
18-
@Query(value = "select * from article a "
19-
+ "where source_id = :sourceId "
20-
+ "order by published_at desc "
21-
+ "limit 1 ", nativeQuery = true)
22-
Optional<Article> findLastPublishedArticle(Long sourceId);
23-
24-
@EntityGraph(attributePaths = {"source", "articleTags", "articleTags.tag"})
25-
@Query("select a from Article a "
26-
+ "where a.isSent = false ")
27-
List<Article> findSendArticles();
28-
29-
@EntityGraph(attributePaths = {"source"})
30-
@Query("select a from Article a "
31-
+ "order by a.publishedAt desc")
32-
List<Article> findNewArticlesTOP6(Pageable pageable);
33-
34-
@EntityGraph(attributePaths = {"source"})
35-
@Query("select distinct a from Article a "
36-
+ "left join a.articleTags at "
37-
+ "left join at.tag t "
38-
+ "where (:keyword is null or lower(a.title) like lower(concat(:keyword, '%'))) "
39-
+ "and (:tag is null or t.tagType = :tag) "
40-
+ "order by a.publishedAt desc")
41-
Page<Article> findArticlesList(Pageable pageable, String keyword, TagType tag);
42-
43-
@Query("select a from Article a "
44-
+ "order by a.publishedAt desc")
45-
List<Article> findDashboardList(Pageable pageable);
10+
public interface ArticleRepository extends JpaRepository<Article, Long>, ArticleRepositoryCustom {
4611

4712
@Query("select a.link from Article a where a.link in :links")
4813
Set<String> findExistsLinks(Set<String> links);
4914

5015
@Query("select count(a.id) from Article a ")
5116
int findTotalArticleSize();
52-
53-
@EntityGraph(attributePaths = {"source"})
54-
@Query("select a from Article a where a.id = :articleId")
55-
Optional<Article> findArticleDetail(Long articleId);
56-
57-
@EntityGraph(attributePaths = {"source"})
58-
@Query("select distinct a from Article a "
59-
+ "join a.articleTags at "
60-
+ "join at.tag t "
61-
+ "where a.id != :articleId "
62-
+ "and t.tagType in :tagTypes "
63-
+ "order by a.publishedAt desc ")
64-
List<Article> findRelatedArticles(Long articleId, List<TagType> tagTypes, Pageable pageable);
6517
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package com.davcatch.devcatch.repository.article;
2+
3+
import java.util.List;
4+
import java.util.Optional;
5+
6+
import org.springframework.data.domain.Page;
7+
import org.springframework.data.domain.Pageable;
8+
9+
import com.davcatch.devcatch.domain.article.Article;
10+
import com.davcatch.devcatch.domain.tag.TagType;
11+
import com.davcatch.devcatch.web.controller.article.response.ArticleDetailResponse;
12+
import com.davcatch.devcatch.web.controller.article.response.ArticleResponse;
13+
14+
public interface ArticleRepositoryCustom {
15+
16+
/**
17+
* 마지막 발행된 아티클 조회
18+
* @param sourceId 해당 소스 ID
19+
* @return 마지막 발행된 아티클
20+
*/
21+
Optional<Article> findLastPublishedArticle(Long sourceId);
22+
23+
/**
24+
* 사용자에게 알림 보낼 아티클 조회
25+
* @return 아티클 리스트
26+
*/
27+
List<Article> findSendArticles();
28+
29+
/**
30+
* 메인 페이지용 최신 아티클 조회
31+
* @return 아티클 리스트
32+
*/
33+
List<ArticleResponse> findNewArticlesTOP6();
34+
35+
/**
36+
* 아티클 검색
37+
* @param pageable 페이징 기본 10개
38+
* @param keyword 키워드
39+
* @param tag 태그
40+
* @return 아티클 리스트
41+
*/
42+
Page<ArticleResponse> findArticlesList(Pageable pageable, String keyword, TagType tag);
43+
44+
/**
45+
* 어드민 대시보드용 아티클 조회
46+
* @param pageable 페이징 기본 5개
47+
* @return 아티클 목록
48+
*/
49+
List<Article> findDashboardList(Pageable pageable);
50+
51+
52+
/**
53+
* 아티클 상세 조회
54+
*
55+
* @param articleId 아티클 ID
56+
* @return 아티클 상세
57+
*/
58+
Optional<ArticleDetailResponse> findArticleDetail(Long articleId);
59+
60+
/**
61+
* 연관된 아티클 조회
62+
* @param articleId 해당 아티클 ID
63+
* @param tagTypes 해당 아티클 TagType 리스트
64+
* @return 연관 아티클 목록
65+
*/
66+
List<ArticleResponse> findRelatedArticles(Long articleId, List<TagType> tagTypes);
67+
}

0 commit comments

Comments
 (0)