Conversation
버스 공지 게시글 조회 시 Article 엔티티를 전체 로드하면서 @PostLoad updateAuthor()가 트리거되어 @OnetoOne LAZY 관계 (koinArticle, koreatechArticle 등)에 대한 N+1 쿼리가 발생하던 문제 수정. 필요한 필드(id, title, createdAt)만 조회하는 BusArticleProjection을 도입해 엔티티 로드 없이 단일 쿼리로 처리되도록 개선. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
📝 WalkthroughWalkthroughIntroduces a new projection DTO for bus articles and updates repository and service code to use that projection with pagination; adjusts Redis model factory to accept primitive fields and persists the selected projection as a Redis notice. Changes
Sequence Diagram(s)sequenceDiagram
participant Scheduler as ArticleSyncService (Scheduler)
participant DB as ArticleRepository (DB)
participant Redis as BusNoticeArticle Repository
Scheduler->>DB: findBusArticlesTop5OrderByCreatedAtDesc(PageRequest.of(0,5))
DB-->>Scheduler: List<BusArticleProjection> (id, title, createdAt)
Scheduler->>Scheduler: select latest projection (by createdAt/title/id)
Scheduler->>Redis: save BusNoticeArticle.of(latest.id, latest.title)
Redis-->>Scheduler: ack
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@src/main/java/in/koreatech/koin/domain/community/article/dto/BusArticleProjection.java`:
- Around line 5-27: The current class-based DTO BusArticleProjection causes
ConverterNotFoundException for the native query used by
findBusArticlesTop5OrderByCreatedAtDesc(); change BusArticleProjection from a
concrete class with fields/constructor/getters into an interface named
BusArticleProjection that declares the getter signatures Integer getId(), String
getTitle(), and LocalDateTime getCreatedAt() so Spring Data can map native query
results by column alias to the projection methods; remove the constructor/fields
and ensure the native query uses column aliases that match these getter names.
In
`@src/main/java/in/koreatech/koin/domain/community/article/service/ArticleSyncService.java`:
- Around line 79-83: The code assumes articles is non-empty and calls
articles.get(0), which can throw IndexOutOfBoundsException; update the
ArticleSyncService logic around
articleRepository.findBusArticlesTop5OrderByCreatedAtDesc() to check if the
returned List<BusArticleProjection> articles is empty before accessing index 0
and handle that case (e.g., return/skip processing or set latestArticles to an
empty list) so subsequent use of latestDate and latestArticles is safe; adjust
the block around articles, latestDate and latestArticles accordingly to
early-return or guard against empty results.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 93439d7d-c1a8-441e-a8fa-593109c7b664
📒 Files selected for processing (4)
src/main/java/in/koreatech/koin/domain/community/article/dto/BusArticleProjection.javasrc/main/java/in/koreatech/koin/domain/community/article/model/redis/BusNoticeArticle.javasrc/main/java/in/koreatech/koin/domain/community/article/repository/ArticleRepository.javasrc/main/java/in/koreatech/koin/domain/community/article/service/ArticleSyncService.java
src/main/java/in/koreatech/koin/domain/community/article/dto/BusArticleProjection.java
Outdated
Show resolved
Hide resolved
src/main/java/in/koreatech/koin/domain/community/article/service/ArticleSyncService.java
Outdated
Show resolved
Hide resolved
src/main/java/in/koreatech/koin/domain/community/article/dto/BusArticleProjection.java
Outdated
Show resolved
Hide resolved
src/main/java/in/koreatech/koin/domain/community/article/model/redis/BusNoticeArticle.java
Outdated
Show resolved
Hide resolved
- BusArticleProjection class → record로 교체 - Native Query + REGEXP → JPQL + NEW 키워드 + LIKE 조건으로 변경 - LIMIT 5 → Pageable(PageRequest.of(0, 5))로 처리 - BusNoticeArticle.from() → of()로 정리 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@src/main/java/in/koreatech/koin/domain/community/article/repository/ArticleRepository.java`:
- Around line 200-202: The current query backing
findBusArticlesTop5OrderByCreatedAtDesc uses only "ORDER BY a.createdAt DESC"
which can produce non-deterministic results when timestamps tie; update the
JPQL/SQL in the repository method to add a deterministic tie-breaker (e.g. ",
a.id DESC" or another unique column) so the top-5 selection is stable across
runs, keeping the method name findBusArticlesTop5OrderByCreatedAtDesc and same
Pageable parameter unchanged.
- Around line 193-200: The JPQL/SQL fragment that selects articles with title
LIKE filters and "a.isNotice = true" is missing the explicit soft-delete
predicate used elsewhere; update the WHERE clause in the query inside
ArticleRepository (the query containing the title LIKE '%통학버스%' ... and
a.isNotice = true) to also include the soft-delete check (e.g. add "AND
a.isDeleted = false" or "AND a.is_deleted = false" to match the repository's
naming convention) so the query is consistent with other repository methods.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: a667d4df-cf3a-4bd1-83e5-3fbedd5f14da
📒 Files selected for processing (4)
src/main/java/in/koreatech/koin/domain/community/article/dto/BusArticleProjection.javasrc/main/java/in/koreatech/koin/domain/community/article/model/redis/BusNoticeArticle.javasrc/main/java/in/koreatech/koin/domain/community/article/repository/ArticleRepository.javasrc/main/java/in/koreatech/koin/domain/community/article/service/ArticleSyncService.java
✅ Files skipped from review due to trivial changes (1)
- src/main/java/in/koreatech/koin/domain/community/article/dto/BusArticleProjection.java
🚧 Files skipped from review as they are similar to previous changes (2)
- src/main/java/in/koreatech/koin/domain/community/article/service/ArticleSyncService.java
- src/main/java/in/koreatech/koin/domain/community/article/model/redis/BusNoticeArticle.java
🔍 개요
updateBusNoticeArticle)에서Article엔티티 전체를 조회하던 쿼리를 Projection으로 개선하여 N+1 문제 및 불필요한 컬럼 조회를 제거합니다.🚀 주요 변경 내용
BusArticleProjection클래스 신규 추가id,title,createdAt필드만 포함하는 Projection DTOArticleRepository.findBusArticlesTop5OrderByCreatedAtDesc()쿼리 수정SELECT *→SELECT a.id, a.title, a.created_at로 필요한 컬럼만 조회List<Article>→List<BusArticleProjection>변경BusNoticeArticle.from(BusArticleProjection)팩토리 메서드 추가ArticleSyncService.updateBusNoticeArticle()에서BusArticleProjection타입으로 처리하도록 변경💬 참고 사항
Article엔티티 전체를 로딩할 경우 연관 엔티티에 의한 N+1 쿼리가 발생할 수 있었으나, Projection 적용으로 필요한 3개 컬럼만 단일 쿼리로 조회합니다.BusArticleProjection은 JPA Native Query 결과 매핑을 위한 일반 클래스로 구현되었습니다 (인터페이스 Projection 대신 생성자 기반 매핑 사용).✅ Checklist (완료 조건)
Summary by CodeRabbit