diff --git a/src/main/java/in/koreatech/koin/domain/community/keyword/repository/UserNotificationStatusRepository.java b/src/main/java/in/koreatech/koin/domain/community/keyword/repository/UserNotificationStatusRepository.java index 9ab80b0e6..1981ab363 100644 --- a/src/main/java/in/koreatech/koin/domain/community/keyword/repository/UserNotificationStatusRepository.java +++ b/src/main/java/in/koreatech/koin/domain/community/keyword/repository/UserNotificationStatusRepository.java @@ -32,9 +32,11 @@ List findUserIdsByNotifiedArticleIdAndUserIdIn( @Modifying(flushAutomatically = true, clearAutomatically = true) @Query(value = """ - INSERT INTO user_notification_status (user_id, last_notified_article_id) - VALUES (:userId, :notifiedArticleId) - ON DUPLICATE KEY UPDATE last_notified_article_id = :notifiedArticleId + INSERT INTO user_notification_status (user_id, last_notified_article_id, created_at, updated_at) + VALUES (:userId, :notifiedArticleId, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) + ON DUPLICATE KEY UPDATE + last_notified_article_id = :notifiedArticleId, + updated_at = CURRENT_TIMESTAMP """, nativeQuery = true) void upsertLastNotifiedArticleId( @Param("userId") Integer userId, diff --git a/src/main/java/in/koreatech/koin/domain/community/keyword/service/KeywordService.java b/src/main/java/in/koreatech/koin/domain/community/keyword/service/KeywordService.java index b8e79e475..a87498e7c 100644 --- a/src/main/java/in/koreatech/koin/domain/community/keyword/service/KeywordService.java +++ b/src/main/java/in/koreatech/koin/domain/community/keyword/service/KeywordService.java @@ -9,6 +9,7 @@ import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import in.koreatech.koin.domain.community.article.exception.ArticleNotFoundException; @@ -206,7 +207,7 @@ public void fetchTopKeywordsFromLastWeek() { } } - @Transactional + @Transactional(propagation = Propagation.REQUIRES_NEW) public void createNotifiedArticleStatus(Integer userId, Integer articleId) { userNotificationStatusRepository.upsertLastNotifiedArticleId(userId, articleId); } diff --git a/src/test/java/in/koreatech/koin/unit/domain/community/keyword/service/KeywordServiceTest.java b/src/test/java/in/koreatech/koin/unit/domain/community/keyword/service/KeywordServiceTest.java index 6ff874eb0..90edc4c71 100644 --- a/src/test/java/in/koreatech/koin/unit/domain/community/keyword/service/KeywordServiceTest.java +++ b/src/test/java/in/koreatech/koin/unit/domain/community/keyword/service/KeywordServiceTest.java @@ -1,11 +1,13 @@ package in.koreatech.koin.unit.domain.community.keyword.service; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import java.lang.reflect.Method; import java.util.List; import java.util.Map; @@ -16,6 +18,8 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.context.ApplicationEventPublisher; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; import in.koreatech.koin.common.event.ArticleKeywordEvent; import in.koreatech.koin.domain.community.article.model.Article; @@ -107,4 +111,14 @@ void createNotifiedArticleStatus_usesAtomicUpsert() { verify(userNotificationStatusRepository).upsertLastNotifiedArticleId(1, 100); } + + @Test + @DisplayName("발송 이력 저장은 항상 새로운 트랜잭션에서 수행한다.") + void createNotifiedArticleStatus_startsNewTransaction() throws NoSuchMethodException { + Method method = KeywordService.class.getMethod("createNotifiedArticleStatus", Integer.class, Integer.class); + Transactional transactional = method.getAnnotation(Transactional.class); + + assertThat(transactional).isNotNull(); + assertThat(transactional.propagation()).isEqualTo(Propagation.REQUIRES_NEW); + } }