From d37f69974bcb34d35c74c9b83946d7dca666fa02 Mon Sep 17 00:00:00 2001 From: belljun3395 <195850@jnu.ac.kr> Date: Wed, 18 Dec 2024 23:07:55 +0900 Subject: [PATCH 01/15] =?UTF-8?q?refactor:=20=EA=B8=B0=EC=A1=B4=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20=EB=B9=88=20DataSource=20=EA=B8=B0?= =?UTF-8?q?=EB=B0=98=EC=9E=84=EC=9D=84=20=EB=82=98=ED=83=80=EB=82=BC=20?= =?UTF-8?q?=EC=88=98=20=EC=9E=88=EB=8F=84=EB=A1=9D=20=EA=B5=AC=EC=B2=B4?= =?UTF-8?q?=EC=A0=81=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/kotlin/repo/config/DataSourceConfig.kt | 12 ++++++------ repo/src/main/kotlin/repo/config/FlywayConfig.kt | 2 +- repo/src/main/kotlin/repo/config/JooqConfig.kt | 13 ++++++++++++- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/repo/src/main/kotlin/repo/config/DataSourceConfig.kt b/repo/src/main/kotlin/repo/config/DataSourceConfig.kt index fba98272..78a69bce 100644 --- a/repo/src/main/kotlin/repo/config/DataSourceConfig.kt +++ b/repo/src/main/kotlin/repo/config/DataSourceConfig.kt @@ -12,14 +12,14 @@ import javax.sql.DataSource @Configuration class DataSourceConfig { companion object { - const val API_DATASOURCE = RepoConfig.BEAN_NAME_PREFIX + "DataSource" - const val API_TX = RepoConfig.BEAN_NAME_PREFIX + "TransactionManager" + const val DATASOURCE = RepoConfig.BEAN_NAME_PREFIX + "DataSource" + const val DATASOURCE_TX = RepoConfig.BEAN_NAME_PREFIX + "DataSourceTransactionManager" } - @Bean(name = [API_DATASOURCE]) + @Bean(name = [DATASOURCE]) @ConfigurationProperties(prefix = "spring.datasource.hikari") - fun apiDataSource(): DataSource = DataSourceBuilder.create().type(HikariDataSource::class.java).build() + fun dataSource(): DataSource = DataSourceBuilder.create().type(HikariDataSource::class.java).build() - @Bean(name = [API_TX]) - fun apiTransactionManager(): PlatformTransactionManager = DataSourceTransactionManager(apiDataSource()) + @Bean(name = [DATASOURCE_TX]) + fun transactionManager(): PlatformTransactionManager = DataSourceTransactionManager(dataSource()) } \ No newline at end of file diff --git a/repo/src/main/kotlin/repo/config/FlywayConfig.kt b/repo/src/main/kotlin/repo/config/FlywayConfig.kt index aaa8a1d8..67a4cb32 100644 --- a/repo/src/main/kotlin/repo/config/FlywayConfig.kt +++ b/repo/src/main/kotlin/repo/config/FlywayConfig.kt @@ -42,7 +42,7 @@ class FlywayConfig { @Bean(name = [FLYWAY_CONFIGURATION]) fun configuration( - @Qualifier(DataSourceConfig.API_DATASOURCE) dataSource: DataSource?, + @Qualifier(DataSourceConfig.DATASOURCE) dataSource: DataSource, ): org.flywaydb.core.api.configuration.Configuration { val configuration = FluentConfiguration() configuration.dataSource(dataSource) diff --git a/repo/src/main/kotlin/repo/config/JooqConfig.kt b/repo/src/main/kotlin/repo/config/JooqConfig.kt index 8ad4178d..42cbaba9 100644 --- a/repo/src/main/kotlin/repo/config/JooqConfig.kt +++ b/repo/src/main/kotlin/repo/config/JooqConfig.kt @@ -4,18 +4,26 @@ import org.jooq.SQLDialect import org.jooq.impl.DataSourceConnectionProvider import org.jooq.impl.DefaultConfiguration import org.jooq.impl.DefaultDSLContext +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.boot.autoconfigure.jooq.SpringTransactionProvider import org.springframework.context.ApplicationEventPublisher import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration +import org.springframework.context.annotation.Import import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator +import org.springframework.transaction.PlatformTransactionManager +import repo.config.DataSourceConfig.Companion.DATASOURCE +import repo.config.DataSourceConfig.Companion.DATASOURCE_TX import repo.flyway.support.ExceptionTranslator import repo.flyway.support.NativeSQLLogger import repo.flyway.support.PerformanceListener import javax.sql.DataSource @Configuration +@Import(DataSourceConfig::class) class JooqConfig( - private val dataSource: DataSource, + @Qualifier(DATASOURCE) private val dataSource: DataSource, + @Qualifier(DATASOURCE_TX) private val txManager: PlatformTransactionManager, private val applicationEventPublisher: ApplicationEventPublisher, ) { companion object { @@ -40,4 +48,7 @@ class JooqConfig( @Bean(name = [JOOQ_CONNECTION_PROVIDER]) fun connectionProvider(): DataSourceConnectionProvider = DataSourceConnectionProvider(dataSource) + + @Bean + fun transactionProvider(): SpringTransactionProvider = SpringTransactionProvider(txManager) } \ No newline at end of file From 0fadaba3ef6f94c08d18fb60203547f15d27fbf9 Mon Sep 17 00:00:00 2001 From: belljun3395 <195850@jnu.ac.kr> Date: Wed, 18 Dec 2024 23:09:06 +0900 Subject: [PATCH 02/15] =?UTF-8?q?feat:=20DataSourceTransactional=20?= =?UTF-8?q?=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repo/jooq/DataSourceTransactional.kt | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 repo/src/main/kotlin/repo/jooq/DataSourceTransactional.kt diff --git a/repo/src/main/kotlin/repo/jooq/DataSourceTransactional.kt b/repo/src/main/kotlin/repo/jooq/DataSourceTransactional.kt new file mode 100644 index 00000000..8cc677bf --- /dev/null +++ b/repo/src/main/kotlin/repo/jooq/DataSourceTransactional.kt @@ -0,0 +1,38 @@ +package repo.jooq + +import org.springframework.core.annotation.AliasFor +import org.springframework.transaction.annotation.Isolation +import org.springframework.transaction.annotation.Propagation +import org.springframework.transaction.annotation.Transactional +import repo.config.DataSourceConfig.Companion.DATASOURCE_TX +import kotlin.reflect.KClass + +/** + * DataSource 트랜잭션을 처리하는 어노테이션 + * transactionManager는 DataSourceConfig에서 설정한 값을 기본으로 사용한다. + */ +@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.RUNTIME) +@Transactional(transactionManager = DATASOURCE_TX) +annotation class DataSourceTransactional( + @get:AliasFor(annotation = Transactional::class, attribute = "label") + val label: Array = [], + @get:AliasFor(annotation = Transactional::class, attribute = "propagation") + val propagation: Propagation = Propagation.REQUIRED, + @get:AliasFor(annotation = Transactional::class, attribute = "isolation") + val isolation: Isolation = Isolation.DEFAULT, + @get:AliasFor(annotation = Transactional::class, attribute = "timeout") + val timeout: Int = -1, + @get:AliasFor(annotation = Transactional::class, attribute = "timeoutString") + val timeoutString: String = "", + @get:AliasFor(annotation = Transactional::class, attribute = "readOnly") + val readOnly: Boolean = false, + @get:AliasFor(annotation = Transactional::class, attribute = "rollbackFor") + val rollbackFor: Array> = [], + @get:AliasFor(annotation = Transactional::class, attribute = "rollbackForClassName") + val rollbackForClassName: Array = [], + @get:AliasFor(annotation = Transactional::class, attribute = "noRollbackFor") + val noRollbackFor: Array> = [], + @get:AliasFor(annotation = Transactional::class, attribute = "noRollbackForClassName") + val noRollbackForClassName: Array = [], +) \ No newline at end of file From 61a145c75c68e416d79fe82a8f9c7abe1cee2a45 Mon Sep 17 00:00:00 2001 From: belljun3395 <195850@jnu.ac.kr> Date: Wed, 18 Dec 2024 23:09:31 +0900 Subject: [PATCH 03/15] =?UTF-8?q?refactor:=20DataSourceTransactional=20?= =?UTF-8?q?=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/few/api/domain/admin/usecase/AddArticleUseCase.kt | 4 ++-- .../com/few/api/domain/admin/usecase/AddWorkbookUseCase.kt | 4 ++-- .../com/few/api/domain/admin/usecase/ConvertContentUseCase.kt | 4 ++-- .../com/few/api/domain/admin/usecase/MapArticleUseCase.kt | 4 ++-- .../com/few/api/domain/admin/usecase/PutImageUseCase.kt | 4 ++-- .../article/event/handler/ArticleViewHisAsyncHandler.kt | 4 ++-- .../few/api/domain/article/usecase/BrowseArticlesUseCase.kt | 4 ++-- .../api/domain/article/usecase/ReadArticleByEmailUseCase.kt | 4 ++-- .../com/few/api/domain/article/usecase/ReadArticleUseCase.kt | 4 ++-- .../article/usecase/transaction/ArticleViewCountTxCase.kt | 4 ++-- .../domain/batch/article/ApiBatchSendArticleEmailService.kt | 4 ++-- .../domain/batch/article/reader/WorkBookSubscriberReader.kt | 4 ++-- .../domain/batch/article/writer/WorkBookSubscriberWriter.kt | 4 ++-- .../kotlin/com/few/api/domain/log/usecase/AddApiLogUseCase.kt | 4 ++-- .../com/few/api/domain/log/usecase/AddEmailLogUseCase.kt | 4 ++-- .../api/domain/member/service/MemberSubscriptionService.kt | 4 ++-- .../com/few/api/domain/member/usecase/DeleteMemberUseCase.kt | 4 ++-- .../com/few/api/domain/member/usecase/SaveMemberUseCase.kt | 4 ++-- .../kotlin/com/few/api/domain/member/usecase/TokenUseCase.kt | 4 ++-- .../api/domain/member/usecase/transaction/SaveMemberTxCase.kt | 4 ++-- .../few/api/domain/problem/usecase/BrowseProblemsUseCase.kt | 4 ++-- .../api/domain/problem/usecase/BrowseUndoneProblemsUseCase.kt | 4 ++-- .../com/few/api/domain/problem/usecase/CheckProblemUseCase.kt | 4 ++-- .../com/few/api/domain/problem/usecase/ReadProblemUseCase.kt | 4 ++-- .../event/handler/SendWorkbookArticleAsyncHandler.kt | 4 ++-- .../subscription/usecase/BrowseSubscribeWorkbooksUseCase.kt | 4 ++-- .../domain/subscription/usecase/SubscribeWorkbookUseCase.kt | 4 ++-- .../api/domain/subscription/usecase/UnsubscribeAllUseCase.kt | 4 ++-- .../domain/subscription/usecase/UnsubscribeWorkbookUseCase.kt | 4 ++-- .../subscription/usecase/UpdateSubscriptionDayUseCase.kt | 4 ++-- .../subscription/usecase/UpdateSubscriptionTimeUseCase.kt | 4 ++-- .../workbook/article/usecase/ReadWorkBookArticleUseCase.kt | 4 ++-- .../few/api/domain/workbook/usecase/BrowseWorkbooksUseCase.kt | 4 ++-- 33 files changed, 66 insertions(+), 66 deletions(-) diff --git a/api/src/main/kotlin/com/few/api/domain/admin/usecase/AddArticleUseCase.kt b/api/src/main/kotlin/com/few/api/domain/admin/usecase/AddArticleUseCase.kt index 1ed3eaa7..99ea255d 100644 --- a/api/src/main/kotlin/com/few/api/domain/admin/usecase/AddArticleUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/admin/usecase/AddArticleUseCase.kt @@ -23,7 +23,7 @@ import com.few.api.domain.problem.repo.command.InsertProblemsCommand import com.few.api.domain.problem.repo.support.Content import com.few.api.domain.problem.repo.support.Contents import org.springframework.stereotype.Component -import org.springframework.transaction.annotation.Transactional +import repo.jooq.DataSourceTransactional import storage.document.PutDocumentProvider import java.io.File import java.time.LocalDateTime @@ -41,7 +41,7 @@ class AddArticleUseCase( private val getUrlService: GetUrlService, private val adminArticleMainCardService: AdminArticleMainCardService, ) { - @Transactional + @DataSourceTransactional fun execute(useCaseIn: AddArticleUseCaseIn): AddArticleUseCaseOut { val writerRecord = memberDao.selectMemberByEmail(SelectMemberByEmailQuery(useCaseIn.writerEmail)) diff --git a/api/src/main/kotlin/com/few/api/domain/admin/usecase/AddWorkbookUseCase.kt b/api/src/main/kotlin/com/few/api/domain/admin/usecase/AddWorkbookUseCase.kt index 4700b7ae..bc562661 100644 --- a/api/src/main/kotlin/com/few/api/domain/admin/usecase/AddWorkbookUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/admin/usecase/AddWorkbookUseCase.kt @@ -6,13 +6,13 @@ import com.few.api.domain.common.exception.InsertException import com.few.api.domain.workbook.repo.WorkbookDao import com.few.api.domain.workbook.repo.command.InsertWorkBookCommand import org.springframework.stereotype.Component -import org.springframework.transaction.annotation.Transactional +import repo.jooq.DataSourceTransactional @Component class AddWorkbookUseCase( private val workbookDao: WorkbookDao, ) { - @Transactional + @DataSourceTransactional fun execute(useCaseIn: AddWorkbookUseCaseIn): AddWorkbookUseCaseOut { val workbookId = workbookDao.insertWorkBook( diff --git a/api/src/main/kotlin/com/few/api/domain/admin/usecase/ConvertContentUseCase.kt b/api/src/main/kotlin/com/few/api/domain/admin/usecase/ConvertContentUseCase.kt index 9eda387d..74e25151 100644 --- a/api/src/main/kotlin/com/few/api/domain/admin/usecase/ConvertContentUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/admin/usecase/ConvertContentUseCase.kt @@ -10,7 +10,7 @@ import com.few.api.domain.admin.utils.ObjectPathGenerator import com.few.api.domain.common.exception.ExternalIntegrationException import com.few.api.domain.common.exception.InsertException import org.springframework.stereotype.Component -import org.springframework.transaction.annotation.Transactional +import repo.jooq.DataSourceTransactional import storage.document.PutDocumentProvider import java.io.File @@ -21,7 +21,7 @@ class ConvertContentUseCase( private val putDocumentService: PutDocumentProvider, private val getUrlService: GetUrlService, ) { - @Transactional + @DataSourceTransactional fun execute(useCaseIn: ConvertContentUseCaseIn): ConvertContentUseCaseOut { val contentSource = useCaseIn.content diff --git a/api/src/main/kotlin/com/few/api/domain/admin/usecase/MapArticleUseCase.kt b/api/src/main/kotlin/com/few/api/domain/admin/usecase/MapArticleUseCase.kt index 1eda89f3..938d1ad4 100644 --- a/api/src/main/kotlin/com/few/api/domain/admin/usecase/MapArticleUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/admin/usecase/MapArticleUseCase.kt @@ -6,14 +6,14 @@ import com.few.api.domain.admin.usecase.dto.MapArticleUseCaseIn import com.few.api.domain.workbook.repo.WorkbookDao import com.few.api.domain.workbook.repo.command.MapWorkBookToArticleCommand import org.springframework.stereotype.Component -import org.springframework.transaction.annotation.Transactional +import repo.jooq.DataSourceTransactional @Component class MapArticleUseCase( private val workbookDao: WorkbookDao, private val adminArticleMainCardService: AdminArticleMainCardService, ) { - @Transactional + @DataSourceTransactional fun execute(useCaseIn: MapArticleUseCaseIn) { workbookDao.mapWorkBookToArticle( MapWorkBookToArticleCommand( diff --git a/api/src/main/kotlin/com/few/api/domain/admin/usecase/PutImageUseCase.kt b/api/src/main/kotlin/com/few/api/domain/admin/usecase/PutImageUseCase.kt index ba8e02e6..197b74aa 100644 --- a/api/src/main/kotlin/com/few/api/domain/admin/usecase/PutImageUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/admin/usecase/PutImageUseCase.kt @@ -12,7 +12,7 @@ import com.few.api.domain.common.exception.InsertException import com.sksamuel.scrimage.ImmutableImage import com.sksamuel.scrimage.webp.WebpWriter import org.springframework.stereotype.Component -import org.springframework.transaction.annotation.Transactional +import repo.jooq.DataSourceTransactional import storage.image.PutImageProvider import java.io.File @@ -22,7 +22,7 @@ class PutImageUseCase( private val putImageService: PutImageProvider, private val getUrlService: GetUrlService, ) { - @Transactional + @DataSourceTransactional fun execute(useCaseIn: PutImageUseCaseIn): PutImageUseCaseOut { val imageSource = useCaseIn.source val suffix = imageSource.originalFilename?.substringAfterLast(".") ?: "jpg" diff --git a/api/src/main/kotlin/com/few/api/domain/article/event/handler/ArticleViewHisAsyncHandler.kt b/api/src/main/kotlin/com/few/api/domain/article/event/handler/ArticleViewHisAsyncHandler.kt index f0b3a81c..10ef9177 100644 --- a/api/src/main/kotlin/com/few/api/domain/article/event/handler/ArticleViewHisAsyncHandler.kt +++ b/api/src/main/kotlin/com/few/api/domain/article/event/handler/ArticleViewHisAsyncHandler.kt @@ -9,7 +9,7 @@ import com.few.api.domain.common.vo.CategoryType import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.scheduling.annotation.Async import org.springframework.stereotype.Component -import org.springframework.transaction.annotation.Transactional +import repo.jooq.DataSourceTransactional @Component class ArticleViewHisAsyncHandler( @@ -19,7 +19,7 @@ class ArticleViewHisAsyncHandler( private val log = KotlinLogging.logger {} @Async(value = DATABASE_ACCESS_POOL) - @Transactional + @DataSourceTransactional fun addArticleViewHis( articleId: Long, memberId: Long, diff --git a/api/src/main/kotlin/com/few/api/domain/article/usecase/BrowseArticlesUseCase.kt b/api/src/main/kotlin/com/few/api/domain/article/usecase/BrowseArticlesUseCase.kt index c15994c1..7eace8e2 100644 --- a/api/src/main/kotlin/com/few/api/domain/article/usecase/BrowseArticlesUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/article/usecase/BrowseArticlesUseCase.kt @@ -12,7 +12,7 @@ import com.few.api.domain.article.usecase.dto.* import com.few.api.domain.common.exception.NotFoundException import com.few.api.domain.common.vo.CategoryType import org.springframework.stereotype.Component -import org.springframework.transaction.annotation.Transactional +import repo.jooq.DataSourceTransactional import java.util.* import kotlin.Comparator @@ -22,7 +22,7 @@ class BrowseArticlesUseCase( private val articleMainCardDao: ArticleMainCardDao, private val articleDao: ArticleDao, ) { - @Transactional(readOnly = true) + @DataSourceTransactional(readOnly = true) fun execute(useCaseIn: ReadArticlesUseCaseIn): ReadArticlesUseCaseOut { /** * 아티클 조회수 테이블에서 마지막 읽은 아티클 아이디, 카테고리를 기반으로 Offset(테이블 row 순위)을 구함 diff --git a/api/src/main/kotlin/com/few/api/domain/article/usecase/ReadArticleByEmailUseCase.kt b/api/src/main/kotlin/com/few/api/domain/article/usecase/ReadArticleByEmailUseCase.kt index 09747d06..4a9d0449 100644 --- a/api/src/main/kotlin/com/few/api/domain/article/usecase/ReadArticleByEmailUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/article/usecase/ReadArticleByEmailUseCase.kt @@ -9,14 +9,14 @@ import com.few.api.domain.article.usecase.dto.ReadArticleByEmailUseCaseIn import com.few.api.domain.common.exception.NotFoundException import com.few.api.domain.common.vo.EmailLogEventType import org.springframework.stereotype.Component -import org.springframework.transaction.annotation.Transactional +import repo.jooq.DataSourceTransactional @Component class ReadArticleByEmailUseCase( private val memberService: ArticleMemberService, private val articleLogService: ArticleLogService, ) { - @Transactional + @DataSourceTransactional fun execute(useCaseIn: ReadArticleByEmailUseCaseIn) { val memberId = memberService.readMemberByEmail(ReadMemberByEmailDto(useCaseIn.destination[0])) diff --git a/api/src/main/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCase.kt b/api/src/main/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCase.kt index 0c151e73..66e34697 100644 --- a/api/src/main/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/article/usecase/ReadArticleUseCase.kt @@ -15,7 +15,7 @@ import com.few.api.domain.common.exception.NotFoundException import com.few.api.domain.common.vo.CategoryType import org.springframework.context.ApplicationEventPublisher import org.springframework.stereotype.Component -import org.springframework.transaction.annotation.Transactional +import repo.jooq.DataSourceTransactional @Component class ReadArticleUseCase( @@ -25,7 +25,7 @@ class ReadArticleUseCase( private val articleViewCountTxCase: ArticleViewCountTxCase, private val applicationEventPublisher: ApplicationEventPublisher, ) { - @Transactional(readOnly = true) + @DataSourceTransactional(readOnly = true) fun execute(useCaseIn: ReadArticleUseCaseIn): ReadArticleUseCaseOut { val articleRecord = articleDao.selectArticleRecord(SelectArticleRecordQuery(useCaseIn.articleId)) diff --git a/api/src/main/kotlin/com/few/api/domain/article/usecase/transaction/ArticleViewCountTxCase.kt b/api/src/main/kotlin/com/few/api/domain/article/usecase/transaction/ArticleViewCountTxCase.kt index edc661de..6befa332 100644 --- a/api/src/main/kotlin/com/few/api/domain/article/usecase/transaction/ArticleViewCountTxCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/article/usecase/transaction/ArticleViewCountTxCase.kt @@ -5,13 +5,13 @@ import com.few.api.domain.article.repo.command.ArticleViewCountCommand import org.springframework.stereotype.Component import org.springframework.transaction.annotation.Isolation import org.springframework.transaction.annotation.Propagation -import org.springframework.transaction.annotation.Transactional +import repo.jooq.DataSourceTransactional @Component class ArticleViewCountTxCase( private val articleViewCountDao: ArticleViewCountDao, ) { - @Transactional(isolation = Isolation.READ_UNCOMMITTED, propagation = Propagation.REQUIRES_NEW) + @DataSourceTransactional(isolation = Isolation.READ_UNCOMMITTED, propagation = Propagation.REQUIRES_NEW) fun browseArticleViewCount(articleId: Long): Long = (articleViewCountDao.selectArticleViewCount(ArticleViewCountCommand(articleId)) ?: 0L) + 1L } \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/domain/batch/article/ApiBatchSendArticleEmailService.kt b/api/src/main/kotlin/com/few/api/domain/batch/article/ApiBatchSendArticleEmailService.kt index 0a36e551..620715c2 100644 --- a/api/src/main/kotlin/com/few/api/domain/batch/article/ApiBatchSendArticleEmailService.kt +++ b/api/src/main/kotlin/com/few/api/domain/batch/article/ApiBatchSendArticleEmailService.kt @@ -5,7 +5,7 @@ import com.few.api.domain.batch.article.reader.WorkBookSubscriberReader import com.few.api.domain.batch.article.writer.WorkBookSubscriberWriter import com.few.api.domain.batch.log.ApiBatchCallExecutionService import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional +import repo.jooq.DataSourceTransactional @Service class ApiBatchSendArticleEmailService( @@ -14,7 +14,7 @@ class ApiBatchSendArticleEmailService( private val batchCallExecutionService: ApiBatchCallExecutionService, private val objectMapper: ObjectMapper, ) { - @Transactional + @DataSourceTransactional fun execute() { val startTime = System.currentTimeMillis() workBookSubscriberReader.execute().let { item -> diff --git a/api/src/main/kotlin/com/few/api/domain/batch/article/reader/WorkBookSubscriberReader.kt b/api/src/main/kotlin/com/few/api/domain/batch/article/reader/WorkBookSubscriberReader.kt index 233bf9c1..edb38480 100644 --- a/api/src/main/kotlin/com/few/api/domain/batch/article/reader/WorkBookSubscriberReader.kt +++ b/api/src/main/kotlin/com/few/api/domain/batch/article/reader/WorkBookSubscriberReader.kt @@ -8,7 +8,7 @@ import org.jooq.Condition import org.jooq.DSLContext import org.jooq.TableField import org.springframework.stereotype.Component -import org.springframework.transaction.annotation.Transactional +import repo.jooq.DataSourceTransactional import java.time.DayOfWeek import java.time.LocalDate import java.time.LocalTime @@ -19,7 +19,7 @@ class WorkBookSubscriberReader( private val dslContext: DSLContext, ) { /** 구독 테이블에서 학습지를 구독하고 있는 회원의 정보를 조회한다.*/ - @Transactional(readOnly = true) + @DataSourceTransactional(readOnly = true) fun execute(): List { val time = LocalTime.now(ZoneId.of("Asia/Seoul")).hour.let { LocalTime.of(it, 0, 0) } val date = LocalDate.now(ZoneId.of("Asia/Seoul")) diff --git a/api/src/main/kotlin/com/few/api/domain/batch/article/writer/WorkBookSubscriberWriter.kt b/api/src/main/kotlin/com/few/api/domain/batch/article/writer/WorkBookSubscriberWriter.kt index e95e6e66..8a583250 100644 --- a/api/src/main/kotlin/com/few/api/domain/batch/article/writer/WorkBookSubscriberWriter.kt +++ b/api/src/main/kotlin/com/few/api/domain/batch/article/writer/WorkBookSubscriberWriter.kt @@ -19,7 +19,7 @@ import org.jooq.DSLContext import org.jooq.InsertSetMoreStep import org.jooq.UpdateConditionStep import org.springframework.stereotype.Component -import org.springframework.transaction.annotation.Transactional +import repo.jooq.DataSourceTransactional import java.time.LocalDate import java.time.LocalDateTime @@ -35,7 +35,7 @@ class WorkBookSubscriberWriter( /** * 구독자들에게 이메일을 전송하고 진행률을 업데이트한다. */ - @Transactional + @DataSourceTransactional fun execute(items: List): Map { val memberIds = items.toMemberIds() val targetWorkBookIds = items.toTargetWorkBookIds() diff --git a/api/src/main/kotlin/com/few/api/domain/log/usecase/AddApiLogUseCase.kt b/api/src/main/kotlin/com/few/api/domain/log/usecase/AddApiLogUseCase.kt index c3f22f9c..1a727aff 100644 --- a/api/src/main/kotlin/com/few/api/domain/log/usecase/AddApiLogUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/log/usecase/AddApiLogUseCase.kt @@ -4,13 +4,13 @@ import com.few.api.domain.log.dto.AddApiLogUseCaseIn import com.few.api.domain.log.repo.LogIfoDao import com.few.api.domain.log.repo.command.InsertLogCommand import org.springframework.stereotype.Component -import org.springframework.transaction.annotation.Transactional +import repo.jooq.DataSourceTransactional @Component class AddApiLogUseCase( private val logIfoDao: LogIfoDao, ) { - @Transactional + @DataSourceTransactional fun execute(useCaseIn: AddApiLogUseCaseIn) { logIfoDao.insertLogIfo(InsertLogCommand(useCaseIn.history)) } diff --git a/api/src/main/kotlin/com/few/api/domain/log/usecase/AddEmailLogUseCase.kt b/api/src/main/kotlin/com/few/api/domain/log/usecase/AddEmailLogUseCase.kt index 73ec14f6..e9424407 100644 --- a/api/src/main/kotlin/com/few/api/domain/log/usecase/AddEmailLogUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/log/usecase/AddEmailLogUseCase.kt @@ -9,14 +9,14 @@ import com.few.api.domain.log.repo.query.SelectEventByMessageIdAndEventTypeQuery import com.few.api.domain.member.repo.MemberDao import com.few.api.domain.member.repo.query.SelectMemberByEmailQuery import org.springframework.stereotype.Component -import org.springframework.transaction.annotation.Transactional +import repo.jooq.DataSourceTransactional @Component class AddEmailLogUseCase( private val memberDao: MemberDao, private val sendArticleEventHistoryDao: SendArticleEventHistoryDao, ) { - @Transactional + @DataSourceTransactional fun execute(useCaseIn: AddEmailLogUseCaseIn) { val (memberId, _, _, _) = memberDao.selectMemberByEmail( diff --git a/api/src/main/kotlin/com/few/api/domain/member/service/MemberSubscriptionService.kt b/api/src/main/kotlin/com/few/api/domain/member/service/MemberSubscriptionService.kt index 108fcd64..c302e5d5 100644 --- a/api/src/main/kotlin/com/few/api/domain/member/service/MemberSubscriptionService.kt +++ b/api/src/main/kotlin/com/few/api/domain/member/service/MemberSubscriptionService.kt @@ -4,13 +4,13 @@ import com.few.api.domain.member.service.dto.DeleteSubscriptionDto import com.few.api.domain.subscription.repo.SubscriptionDao import com.few.api.domain.subscription.repo.command.UpdateDeletedAtInAllSubscriptionCommand import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional +import repo.jooq.DataSourceTransactional @Service class MemberSubscriptionService( private val subscriptionDao: SubscriptionDao, ) { - @Transactional + @DataSourceTransactional fun deleteSubscription(dto: DeleteSubscriptionDto) { subscriptionDao.updateDeletedAtInAllSubscription( UpdateDeletedAtInAllSubscriptionCommand( diff --git a/api/src/main/kotlin/com/few/api/domain/member/usecase/DeleteMemberUseCase.kt b/api/src/main/kotlin/com/few/api/domain/member/usecase/DeleteMemberUseCase.kt index b7199fa0..92580716 100644 --- a/api/src/main/kotlin/com/few/api/domain/member/usecase/DeleteMemberUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/member/usecase/DeleteMemberUseCase.kt @@ -7,14 +7,14 @@ import com.few.api.domain.member.service.dto.DeleteSubscriptionDto import com.few.api.domain.member.usecase.dto.DeleteMemberUseCaseIn import com.few.api.domain.member.usecase.dto.DeleteMemberUseCaseOut import org.springframework.stereotype.Component -import org.springframework.transaction.annotation.Transactional +import repo.jooq.DataSourceTransactional @Component class DeleteMemberUseCase( private val memberDao: MemberDao, private val memberSubscriptionService: MemberSubscriptionService, ) { - @Transactional + @DataSourceTransactional fun execute(useCaseIn: DeleteMemberUseCaseIn): DeleteMemberUseCaseOut { memberDao.deleteMember( DeleteMemberCommand( diff --git a/api/src/main/kotlin/com/few/api/domain/member/usecase/SaveMemberUseCase.kt b/api/src/main/kotlin/com/few/api/domain/member/usecase/SaveMemberUseCase.kt index 66eff647..7e700bd9 100644 --- a/api/src/main/kotlin/com/few/api/domain/member/usecase/SaveMemberUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/member/usecase/SaveMemberUseCase.kt @@ -10,7 +10,7 @@ import com.few.api.domain.member.usecase.dto.SaveMemberUseCaseIn import com.few.api.domain.member.usecase.dto.SaveMemberUseCaseOut import com.few.api.domain.member.usecase.transaction.SaveMemberTxCase import org.springframework.stereotype.Component -import org.springframework.transaction.annotation.Transactional +import repo.jooq.DataSourceTransactional import security.encryptor.IdEncryptor import java.net.URL @@ -21,7 +21,7 @@ class SaveMemberUseCase( private val idEncryption: IdEncryptor, private val saveMemberTxCase: SaveMemberTxCase, ) { - @Transactional + @DataSourceTransactional fun execute(useCaseIn: SaveMemberUseCaseIn): SaveMemberUseCaseOut { /** email을 통해 가입 이력이 있는지 확인 */ val (headComment, subComment, memberId) = diff --git a/api/src/main/kotlin/com/few/api/domain/member/usecase/TokenUseCase.kt b/api/src/main/kotlin/com/few/api/domain/member/usecase/TokenUseCase.kt index 26d05652..71b241d0 100644 --- a/api/src/main/kotlin/com/few/api/domain/member/usecase/TokenUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/member/usecase/TokenUseCase.kt @@ -8,7 +8,7 @@ import com.few.api.domain.member.repo.command.UpdateMemberTypeCommand import com.few.api.domain.member.usecase.dto.TokenUseCaseIn import com.few.api.domain.member.usecase.dto.TokenUseCaseOut import org.springframework.stereotype.Component -import org.springframework.transaction.annotation.Transactional +import repo.jooq.DataSourceTransactional import security.Roles import security.TokenGenerator import security.TokenResolver @@ -22,7 +22,7 @@ class TokenUseCase( private val memberDao: MemberDao, private val idEncryption: IdEncryptor, ) { - @Transactional + @DataSourceTransactional fun execute(useCaseIn: TokenUseCaseIn): TokenUseCaseOut { var isLogin = true diff --git a/api/src/main/kotlin/com/few/api/domain/member/usecase/transaction/SaveMemberTxCase.kt b/api/src/main/kotlin/com/few/api/domain/member/usecase/transaction/SaveMemberTxCase.kt index 0f58c83b..07de27ef 100644 --- a/api/src/main/kotlin/com/few/api/domain/member/usecase/transaction/SaveMemberTxCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/member/usecase/transaction/SaveMemberTxCase.kt @@ -9,7 +9,7 @@ import com.few.api.domain.member.repo.record.MemberIdAndIsDeletedRecord import com.few.api.domain.member.usecase.dto.SaveMemberTxCaseIn import com.few.api.domain.member.usecase.dto.SaveMemberTxCaseOut import org.springframework.stereotype.Component -import org.springframework.transaction.annotation.Transactional +import repo.jooq.DataSourceTransactional @Component class SaveMemberTxCase( @@ -22,7 +22,7 @@ class SaveMemberTxCase( private const val SIGNUP_SUB_COMMENT = "가입하신 이메일 주소를 확인해주세요." } - @Transactional + @DataSourceTransactional fun execute(dto: SaveMemberTxCaseIn): SaveMemberTxCaseOut = dto.record?.let { signUpBeforeMember -> signUpBeforeMember.takeIf { it.isDeleted }?.let { diff --git a/api/src/main/kotlin/com/few/api/domain/problem/usecase/BrowseProblemsUseCase.kt b/api/src/main/kotlin/com/few/api/domain/problem/usecase/BrowseProblemsUseCase.kt index 036dfb42..1c72fd4b 100644 --- a/api/src/main/kotlin/com/few/api/domain/problem/usecase/BrowseProblemsUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/problem/usecase/BrowseProblemsUseCase.kt @@ -6,13 +6,13 @@ import com.few.api.domain.problem.repo.query.SelectProblemsByArticleIdQuery import com.few.api.domain.problem.usecase.dto.BrowseProblemsUseCaseIn import com.few.api.domain.problem.usecase.dto.BrowseProblemsUseCaseOut import org.springframework.stereotype.Component -import org.springframework.transaction.annotation.Transactional +import repo.jooq.DataSourceTransactional @Component class BrowseProblemsUseCase( private val problemDao: ProblemDao, ) { - @Transactional(readOnly = true) + @DataSourceTransactional(readOnly = true) fun execute(useCaseIn: BrowseProblemsUseCaseIn): BrowseProblemsUseCaseOut { problemDao .selectProblemsByArticleId(SelectProblemsByArticleIdQuery(useCaseIn.articleId)) diff --git a/api/src/main/kotlin/com/few/api/domain/problem/usecase/BrowseUndoneProblemsUseCase.kt b/api/src/main/kotlin/com/few/api/domain/problem/usecase/BrowseUndoneProblemsUseCase.kt index 0d9eac24..e6a8554e 100644 --- a/api/src/main/kotlin/com/few/api/domain/problem/usecase/BrowseUndoneProblemsUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/problem/usecase/BrowseUndoneProblemsUseCase.kt @@ -12,7 +12,7 @@ import com.few.api.domain.problem.service.dto.BrowseWorkbookIdAndProgressInDto import com.few.api.domain.problem.usecase.dto.BrowseProblemsUseCaseOut import com.few.api.domain.problem.usecase.dto.BrowseUndoneProblemsUseCaseIn import org.springframework.stereotype.Component -import org.springframework.transaction.annotation.Transactional +import repo.jooq.DataSourceTransactional @Component class BrowseUndoneProblemsUseCase( @@ -21,7 +21,7 @@ class BrowseUndoneProblemsUseCase( private val problemArticleService: ProblemArticleService, private val submitHistoryDao: SubmitHistoryDao, ) { - @Transactional(readOnly = true) + @DataSourceTransactional(readOnly = true) fun execute(useCaseIn: BrowseUndoneProblemsUseCaseIn): BrowseProblemsUseCaseOut { /** * 유저가 구독한 워크북들에 속한 아티클 개수를 조회함 diff --git a/api/src/main/kotlin/com/few/api/domain/problem/usecase/CheckProblemUseCase.kt b/api/src/main/kotlin/com/few/api/domain/problem/usecase/CheckProblemUseCase.kt index e7c19da7..0456bfec 100644 --- a/api/src/main/kotlin/com/few/api/domain/problem/usecase/CheckProblemUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/problem/usecase/CheckProblemUseCase.kt @@ -9,14 +9,14 @@ import com.few.api.domain.problem.repo.query.SelectProblemAnswerQuery import com.few.api.domain.problem.usecase.dto.CheckProblemUseCaseIn import com.few.api.domain.problem.usecase.dto.CheckProblemUseCaseOut import org.springframework.stereotype.Component -import org.springframework.transaction.annotation.Transactional +import repo.jooq.DataSourceTransactional @Component class CheckProblemUseCase( private val problemDao: ProblemDao, private val submitHistoryDao: SubmitHistoryDao, ) { - @Transactional + @DataSourceTransactional fun execute(useCaseIn: CheckProblemUseCaseIn): CheckProblemUseCaseOut { val memberId = useCaseIn.memberId val problemId = useCaseIn.problemId diff --git a/api/src/main/kotlin/com/few/api/domain/problem/usecase/ReadProblemUseCase.kt b/api/src/main/kotlin/com/few/api/domain/problem/usecase/ReadProblemUseCase.kt index f10b122d..4fec2136 100644 --- a/api/src/main/kotlin/com/few/api/domain/problem/usecase/ReadProblemUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/problem/usecase/ReadProblemUseCase.kt @@ -8,14 +8,14 @@ import com.few.api.domain.problem.usecase.dto.ReadProblemContentsUseCaseOutDetai import com.few.api.domain.problem.usecase.dto.ReadProblemUseCaseIn import com.few.api.domain.problem.usecase.dto.ReadProblemUseCaseOut import org.springframework.stereotype.Component -import org.springframework.transaction.annotation.Transactional +import repo.jooq.DataSourceTransactional @Component class ReadProblemUseCase( private val problemDao: ProblemDao, private val contentsJsonMapper: ContentsJsonMapper, ) { - @Transactional(readOnly = true) + @DataSourceTransactional(readOnly = true) fun execute(useCaseIn: ReadProblemUseCaseIn): ReadProblemUseCaseOut { val problemId = useCaseIn.problemId diff --git a/api/src/main/kotlin/com/few/api/domain/subscription/event/handler/SendWorkbookArticleAsyncHandler.kt b/api/src/main/kotlin/com/few/api/domain/subscription/event/handler/SendWorkbookArticleAsyncHandler.kt index 6bdbb340..8061fe8a 100644 --- a/api/src/main/kotlin/com/few/api/domain/subscription/event/handler/SendWorkbookArticleAsyncHandler.kt +++ b/api/src/main/kotlin/com/few/api/domain/subscription/event/handler/SendWorkbookArticleAsyncHandler.kt @@ -16,7 +16,7 @@ import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.scheduling.annotation.Async import org.springframework.stereotype.Component import org.springframework.transaction.annotation.Propagation -import org.springframework.transaction.annotation.Transactional +import repo.jooq.DataSourceTransactional import java.time.LocalDate @Component @@ -32,7 +32,7 @@ class SendWorkbookArticleAsyncHandler( @Async(value = DATABASE_ACCESS_POOL) @ApiLockFor(identifier = ApiLockIdentifier.SUBSCRIPTION_MEMBER_ID_WORKBOOK_ID) - @Transactional(propagation = Propagation.REQUIRES_NEW) + @DataSourceTransactional(propagation = Propagation.REQUIRES_NEW) fun sendWorkbookArticle( memberId: Long, workbookId: Long, diff --git a/api/src/main/kotlin/com/few/api/domain/subscription/usecase/BrowseSubscribeWorkbooksUseCase.kt b/api/src/main/kotlin/com/few/api/domain/subscription/usecase/BrowseSubscribeWorkbooksUseCase.kt index e209ea52..84762558 100644 --- a/api/src/main/kotlin/com/few/api/domain/subscription/usecase/BrowseSubscribeWorkbooksUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/subscription/usecase/BrowseSubscribeWorkbooksUseCase.kt @@ -15,7 +15,7 @@ import com.few.api.domain.subscription.service.dto.ReadAllWorkbookTitleInDto import com.few.api.domain.subscription.service.dto.ReadArticleIdByWorkbookIdAndDayDto import com.few.api.domain.subscription.usecase.dto.* import org.springframework.stereotype.Component -import org.springframework.transaction.annotation.Transactional +import repo.jooq.DataSourceTransactional import java.lang.IllegalStateException @Suppress("ktlint:standard:class-naming") @@ -51,7 +51,7 @@ class BrowseSubscribeWorkbooksUseCase( private val subscriptionWorkbookService: SubscriptionWorkbookService, private val objectMapper: ObjectMapper, ) { - @Transactional + @DataSourceTransactional fun execute(useCaseIn: BrowseSubscribeWorkbooksUseCaseIn): BrowseSubscribeWorkbooksUseCaseOut { val strategy = when (useCaseIn.view) { diff --git a/api/src/main/kotlin/com/few/api/domain/subscription/usecase/SubscribeWorkbookUseCase.kt b/api/src/main/kotlin/com/few/api/domain/subscription/usecase/SubscribeWorkbookUseCase.kt index 3f621605..882ec542 100644 --- a/api/src/main/kotlin/com/few/api/domain/subscription/usecase/SubscribeWorkbookUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/subscription/usecase/SubscribeWorkbookUseCase.kt @@ -16,7 +16,7 @@ import com.few.api.domain.subscription.usecase.model.WorkbookSubscriptionHistory import com.few.api.domain.subscription.usecase.model.WorkbookSubscriptionStatus import org.springframework.context.ApplicationEventPublisher import org.springframework.stereotype.Component -import org.springframework.transaction.annotation.Transactional +import repo.jooq.DataSourceTransactional @Component class SubscribeWorkbookUseCase( @@ -24,7 +24,7 @@ class SubscribeWorkbookUseCase( private val applicationEventPublisher: ApplicationEventPublisher, ) { @ApiLockFor(ApiLockIdentifier.SUBSCRIPTION_MEMBER_ID_WORKBOOK_ID) - @Transactional + @DataSourceTransactional fun execute(useCaseIn: SubscribeWorkbookUseCaseIn) { val subTargetWorkbookId = useCaseIn.workbookId val memberId = useCaseIn.memberId diff --git a/api/src/main/kotlin/com/few/api/domain/subscription/usecase/UnsubscribeAllUseCase.kt b/api/src/main/kotlin/com/few/api/domain/subscription/usecase/UnsubscribeAllUseCase.kt index 37a2bd52..7975acff 100644 --- a/api/src/main/kotlin/com/few/api/domain/subscription/usecase/UnsubscribeAllUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/subscription/usecase/UnsubscribeAllUseCase.kt @@ -4,13 +4,13 @@ import com.few.api.domain.subscription.repo.SubscriptionDao import com.few.api.domain.subscription.repo.command.UpdateDeletedAtInAllSubscriptionCommand import com.few.api.domain.subscription.usecase.dto.UnsubscribeAllUseCaseIn import org.springframework.stereotype.Component -import org.springframework.transaction.annotation.Transactional +import repo.jooq.DataSourceTransactional @Component class UnsubscribeAllUseCase( private val subscriptionDao: SubscriptionDao, ) { - @Transactional + @DataSourceTransactional fun execute(useCaseIn: UnsubscribeAllUseCaseIn) { // TODO: request sending email var opinion = useCaseIn.opinion diff --git a/api/src/main/kotlin/com/few/api/domain/subscription/usecase/UnsubscribeWorkbookUseCase.kt b/api/src/main/kotlin/com/few/api/domain/subscription/usecase/UnsubscribeWorkbookUseCase.kt index 16c95ea6..d1bc87e1 100644 --- a/api/src/main/kotlin/com/few/api/domain/subscription/usecase/UnsubscribeWorkbookUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/subscription/usecase/UnsubscribeWorkbookUseCase.kt @@ -4,14 +4,14 @@ import com.few.api.domain.subscription.repo.SubscriptionDao import com.few.api.domain.subscription.repo.command.UpdateDeletedAtInWorkbookSubscriptionCommand import com.few.api.domain.subscription.usecase.dto.UnsubscribeWorkbookUseCaseIn import org.springframework.stereotype.Component -import org.springframework.transaction.annotation.Transactional +import repo.jooq.DataSourceTransactional @Component class UnsubscribeWorkbookUseCase( private val subscriptionDao: SubscriptionDao, ) { // todo add test - @Transactional + @DataSourceTransactional fun execute(useCaseIn: UnsubscribeWorkbookUseCaseIn) { // TODO: request sending email var opinion = useCaseIn.opinion diff --git a/api/src/main/kotlin/com/few/api/domain/subscription/usecase/UpdateSubscriptionDayUseCase.kt b/api/src/main/kotlin/com/few/api/domain/subscription/usecase/UpdateSubscriptionDayUseCase.kt index f6e1d144..0954b315 100644 --- a/api/src/main/kotlin/com/few/api/domain/subscription/usecase/UpdateSubscriptionDayUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/subscription/usecase/UpdateSubscriptionDayUseCase.kt @@ -4,13 +4,13 @@ import com.few.api.domain.subscription.repo.SubscriptionDao import com.few.api.domain.subscription.repo.command.BulkUpdateSubscriptionSendDayCommand import com.few.api.domain.subscription.usecase.dto.UpdateSubscriptionDayUseCaseIn import org.springframework.stereotype.Component -import org.springframework.transaction.annotation.Transactional +import repo.jooq.DataSourceTransactional @Component class UpdateSubscriptionDayUseCase( private val subscriptionDao: SubscriptionDao, ) { - @Transactional + @DataSourceTransactional fun execute(useCaseIn: UpdateSubscriptionDayUseCaseIn) { /** * workbookId기 없으면, memberId로 구독중인 모든 workbookId를 가져와서 해당하는 모든 workbookId의 구독요일을 변경한다. diff --git a/api/src/main/kotlin/com/few/api/domain/subscription/usecase/UpdateSubscriptionTimeUseCase.kt b/api/src/main/kotlin/com/few/api/domain/subscription/usecase/UpdateSubscriptionTimeUseCase.kt index 931531b1..a741bab8 100644 --- a/api/src/main/kotlin/com/few/api/domain/subscription/usecase/UpdateSubscriptionTimeUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/subscription/usecase/UpdateSubscriptionTimeUseCase.kt @@ -4,13 +4,13 @@ import com.few.api.domain.subscription.repo.SubscriptionDao import com.few.api.domain.subscription.repo.command.BulkUpdateSubscriptionSendTimeCommand import com.few.api.domain.subscription.usecase.dto.UpdateSubscriptionTimeUseCaseIn import org.springframework.stereotype.Component -import org.springframework.transaction.annotation.Transactional +import repo.jooq.DataSourceTransactional @Component class UpdateSubscriptionTimeUseCase( private val subscriptionDao: SubscriptionDao, ) { - @Transactional + @DataSourceTransactional fun execute(useCaseIn: UpdateSubscriptionTimeUseCaseIn) { /** * workbookId기 없으면, memberId로 구독중인 모든 workbookId를 가져와서 해당하는 모든 workbookId의 구독요일을 변경한다. diff --git a/api/src/main/kotlin/com/few/api/domain/workbook/article/usecase/ReadWorkBookArticleUseCase.kt b/api/src/main/kotlin/com/few/api/domain/workbook/article/usecase/ReadWorkBookArticleUseCase.kt index 2ea01a95..b032be2f 100644 --- a/api/src/main/kotlin/com/few/api/domain/workbook/article/usecase/ReadWorkBookArticleUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/workbook/article/usecase/ReadWorkBookArticleUseCase.kt @@ -15,7 +15,7 @@ import com.few.api.domain.workbook.article.dto.ReadWorkBookArticleUseCaseIn import com.few.api.domain.workbook.article.dto.WriterDetail import org.springframework.context.ApplicationEventPublisher import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional +import repo.jooq.DataSourceTransactional @Service class ReadWorkBookArticleUseCase( @@ -25,7 +25,7 @@ class ReadWorkBookArticleUseCase( private val articleViewCountTxCase: ArticleViewCountTxCase, private val applicationEventPublisher: ApplicationEventPublisher, ) { - @Transactional(readOnly = true) + @DataSourceTransactional(readOnly = true) fun execute(useCaseIn: ReadWorkBookArticleUseCaseIn): ReadWorkBookArticleOut { val articleRecord = articleDao.selectWorkBookArticleRecord( diff --git a/api/src/main/kotlin/com/few/api/domain/workbook/usecase/BrowseWorkbooksUseCase.kt b/api/src/main/kotlin/com/few/api/domain/workbook/usecase/BrowseWorkbooksUseCase.kt index c1d0c153..6c8f1668 100644 --- a/api/src/main/kotlin/com/few/api/domain/workbook/usecase/BrowseWorkbooksUseCase.kt +++ b/api/src/main/kotlin/com/few/api/domain/workbook/usecase/BrowseWorkbooksUseCase.kt @@ -17,7 +17,7 @@ import com.few.api.domain.workbook.usecase.dto.WriterDetail import com.few.api.domain.workbook.usecase.model.* import com.few.api.domain.workbook.usecase.model.order.* import org.springframework.stereotype.Component -import org.springframework.transaction.annotation.Transactional +import repo.jooq.DataSourceTransactional enum class WorkBookOrderStrategy { BASIC, @@ -43,7 +43,7 @@ class BrowseWorkbooksUseCase( private val workbookMemberService: WorkbookMemberService, private val workbookSubscribeService: WorkbookSubscribeService, ) { - @Transactional + @DataSourceTransactional fun execute(useCaseIn: BrowseWorkbooksUseCaseIn): BrowseWorkbooksUseCaseOut { val workbookRecords = workbookDao.browseWorkBookWithSubscriptionCount( From af53caf6b7dea9906d4b793a9b58d006cadc3869 Mon Sep 17 00:00:00 2001 From: belljun3395 <195850@jnu.ac.kr> Date: Wed, 18 Dec 2024 23:11:13 +0900 Subject: [PATCH 04/15] =?UTF-8?q?feat:=20JPA=20=EA=B4=80=EB=A0=A8=20allOpe?= =?UTF-8?q?n=20=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 6181afa1..237d21e8 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -126,6 +126,9 @@ subprojects { * jpa meta-annotations not automatically opened through the default settings of the plugin.spring */ allOpen { + annotation("jakarta.persistence.Entity") + annotation("jakarta.persistence.MappedSuperclass") + annotation("jakarta.persistence.Embeddable") } dependencyManagement { @@ -334,11 +337,6 @@ subprojects { defaultTasks("bootRun") } -/** do all copy data migration */ -tasks.register("copyDataMigrationAll") { - dependsOn(":api:copyDataMigration") -} - /** do all jooq codegen */ tasks.register("jooqCodegenAll") { dependsOn("copyDataMigrationAll") From 8076cdbceffe99b470b3db3489db450c9ead2ce9 Mon Sep 17 00:00:00 2001 From: belljun3395 <195850@jnu.ac.kr> Date: Wed, 18 Dec 2024 23:11:54 +0900 Subject: [PATCH 05/15] =?UTF-8?q?feat:=20Jpa=20=EC=9D=98=EC=A1=B4=EC=84=B1?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- repo/build.gradle.kts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/repo/build.gradle.kts b/repo/build.gradle.kts index d49edaef..78971c20 100644 --- a/repo/build.gradle.kts +++ b/repo/build.gradle.kts @@ -16,6 +16,9 @@ dependencies { /** jooq */ api("org.springframework.boot:spring-boot-starter-jooq") + /** jpa */ + api("org.springframework.boot:spring-boot-starter-data-jpa") + /** flyway */ implementation("org.flywaydb:flyway-core:${DependencyVersion.FLYWAY}") implementation("org.flywaydb:flyway-mysql") From 06490fee0070890632a48da4f6d24a70c8caa1e1 Mon Sep 17 00:00:00 2001 From: belljun3395 <195850@jnu.ac.kr> Date: Wed, 18 Dec 2024 23:12:25 +0900 Subject: [PATCH 06/15] =?UTF-8?q?feat:=20Jpa=20=ED=95=84=EC=9A=94=20?= =?UTF-8?q?=ED=94=84=EB=A1=9C=ED=8D=BC=ED=8B=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/src/test/resources/application-test.yml | 6 ++++++ repo/src/main/resources/application-api-repo-local.yml | 6 ++++++ repo/src/main/resources/application-api-repo-prd.yml | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/api/src/test/resources/application-test.yml b/api/src/test/resources/application-test.yml index bbd73bb8..ac826e90 100644 --- a/api/src/test/resources/application-test.yml +++ b/api/src/test/resources/application-test.yml @@ -17,6 +17,12 @@ spring: sql-migration-suffixes: sql baseline-on-migrate: true baseline-version: 0 + jpa: + hibernate: + ddl-auto: validate + properties: + hibernate: + dialect: org.hibernate.dialect.MySQL8Dialect mail: protocol: smtp host: smtp.gmail.com diff --git a/repo/src/main/resources/application-api-repo-local.yml b/repo/src/main/resources/application-api-repo-local.yml index 14bd2167..e4ad0cd5 100644 --- a/repo/src/main/resources/application-api-repo-local.yml +++ b/repo/src/main/resources/application-api-repo-local.yml @@ -17,3 +17,9 @@ spring: sql-migration-suffixes: sql baseline-on-migrate: true baseline-version: 0 + jpa: + hibernate: + ddl-auto: validate + properties: + hibernate: + dialect: org.hibernate.dialect.MySQL8Dialect diff --git a/repo/src/main/resources/application-api-repo-prd.yml b/repo/src/main/resources/application-api-repo-prd.yml index e22587a0..c5b803c0 100644 --- a/repo/src/main/resources/application-api-repo-prd.yml +++ b/repo/src/main/resources/application-api-repo-prd.yml @@ -17,3 +17,9 @@ spring: sql-migration-suffixes: sql baseline-on-migrate: true baseline-version: 0 + jpa: + hibernate: + ddl-auto: validate + properties: + hibernate: + dialect: org.hibernate.dialect.MySQL8Dialect From adb9574e98226d10ecccf02bd0bf575669d9dc3d Mon Sep 17 00:00:00 2001 From: belljun3395 <195850@jnu.ac.kr> Date: Wed, 18 Dec 2024 23:13:00 +0900 Subject: [PATCH 07/15] =?UTF-8?q?feat:=20JpaConfig=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- repo/src/main/kotlin/repo/config/JpaConfig.kt | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 repo/src/main/kotlin/repo/config/JpaConfig.kt diff --git a/repo/src/main/kotlin/repo/config/JpaConfig.kt b/repo/src/main/kotlin/repo/config/JpaConfig.kt new file mode 100644 index 00000000..dd975457 --- /dev/null +++ b/repo/src/main/kotlin/repo/config/JpaConfig.kt @@ -0,0 +1,65 @@ +package repo.config + +import jakarta.persistence.EntityManagerFactory +import org.springframework.boot.autoconfigure.EnableAutoConfiguration +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration +import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration +import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration +import org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties +import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings +import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties +import org.springframework.boot.context.properties.ConfigurationProperties +import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.data.jpa.repository.config.EnableJpaAuditing +import org.springframework.orm.jpa.JpaTransactionManager +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean +import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter +import org.springframework.transaction.PlatformTransactionManager +import org.springframework.transaction.annotation.EnableTransactionManagement +import javax.sql.DataSource + +@Configuration +@EnableAutoConfiguration( + exclude = [ + DataSourceAutoConfiguration::class, + DataSourceTransactionManagerAutoConfiguration::class, + HibernateJpaAutoConfiguration::class, + ], +) +@EnableJpaAuditing +@EnableTransactionManagement +class JpaConfig { + companion object { + const val ENTITY_UNIT = "few" + const val ENTITY_PACKAGE = "com.few" + const val JPA_TX = RepoConfig.BEAN_NAME_PREFIX + "JpaTransactionManager" + const val JPA_EMF = RepoConfig.BEAN_NAME_PREFIX + "JpaEntityManagerFactory" + } + + @Bean(name = [JPA_EMF]) + fun entityManagerFactory(dataSource: DataSource): LocalContainerEntityManagerFactoryBean { + val jpaPropertyMap = jpaProperties().properties + val hibernatePropertyMap = + hibernateProperties().determineHibernateProperties(jpaPropertyMap, HibernateSettings()) + + return EntityManagerFactoryBuilder(HibernateJpaVendorAdapter(), jpaPropertyMap, null) + .dataSource(dataSource) + .properties(hibernatePropertyMap) + .persistenceUnit(ENTITY_UNIT) + .packages(ENTITY_PACKAGE) + .build() + } + + @Bean(name = [JPA_TX]) + fun jpaTransactionManager(emf: EntityManagerFactory): PlatformTransactionManager = JpaTransactionManager(emf) + + @Bean + @ConfigurationProperties(prefix = "spring.jpa") + fun jpaProperties(): JpaProperties = JpaProperties() + + @Bean + @ConfigurationProperties(prefix = "spring.jpa.hibernate") + fun hibernateProperties(): HibernateProperties = HibernateProperties() +} \ No newline at end of file From 4c68f5342a8e165c27ba9584caa2a38de79b48f5 Mon Sep 17 00:00:00 2001 From: belljun3395 <195850@jnu.ac.kr> Date: Wed, 18 Dec 2024 23:14:33 +0900 Subject: [PATCH 08/15] =?UTF-8?q?feat:=20Jpa=20=EC=B6=94=EA=B0=80=EB=A1=9C?= =?UTF-8?q?=20=EC=A4=91=EB=B3=B5=EB=90=98=EB=8A=94=20=EB=B9=88=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/main/kotlin/web/security/config/WebSecurityConfig.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/web/src/main/kotlin/web/security/config/WebSecurityConfig.kt b/web/src/main/kotlin/web/security/config/WebSecurityConfig.kt index 507bb3b4..0d80e87c 100644 --- a/web/src/main/kotlin/web/security/config/WebSecurityConfig.kt +++ b/web/src/main/kotlin/web/security/config/WebSecurityConfig.kt @@ -53,8 +53,9 @@ class WebSecurityConfig { } @Bean(name = [WEB_SECURITY_CONFIGURER]) - fun webSecurityConfigurer(userArgumentHandlerMethodArgumentResolver: HandlerMethodArgumentResolver): WebMvcConfigurer = - WebSecurityConfigurer(userArgumentHandlerMethodArgumentResolver) + fun webSecurityConfigurer( + @Qualifier(USER_ARGUMENT_HANDLER_METHOD_ARGUMENT_RESOLVER) userArgumentHandlerMethodArgumentResolver: HandlerMethodArgumentResolver, + ): WebMvcConfigurer = WebSecurityConfigurer(userArgumentHandlerMethodArgumentResolver) @Profile("local") @Bean(name = ["local$SECURITY_FILTER_CHAIN"]) From 9dfa66fd9467f3e0646114fff5f20b1e575d6db8 Mon Sep 17 00:00:00 2001 From: belljun3395 <195850@jnu.ac.kr> Date: Wed, 18 Dec 2024 23:13:47 +0900 Subject: [PATCH 09/15] =?UTF-8?q?feat:=20Jpa=20=ED=8E=B8=EC=9D=98=20?= =?UTF-8?q?=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - EnableJpaRepositories - EntityTimeJsonFormat - JpaTransactional --- .../kotlin/repo/jpa/EnableJpaRepositories.kt | 51 +++++++++++++++++++ .../kotlin/repo/jpa/EntityTimeJsonFormat.kt | 20 ++++++++ .../main/kotlin/repo/jpa/JpaTransactional.kt | 38 ++++++++++++++ 3 files changed, 109 insertions(+) create mode 100644 repo/src/main/kotlin/repo/jpa/EnableJpaRepositories.kt create mode 100644 repo/src/main/kotlin/repo/jpa/EntityTimeJsonFormat.kt create mode 100644 repo/src/main/kotlin/repo/jpa/JpaTransactional.kt diff --git a/repo/src/main/kotlin/repo/jpa/EnableJpaRepositories.kt b/repo/src/main/kotlin/repo/jpa/EnableJpaRepositories.kt new file mode 100644 index 00000000..57c12e10 --- /dev/null +++ b/repo/src/main/kotlin/repo/jpa/EnableJpaRepositories.kt @@ -0,0 +1,51 @@ +package repo.jpa + +import org.springframework.context.annotation.ComponentScan +import org.springframework.core.annotation.AliasFor +import org.springframework.data.jpa.repository.config.EnableJpaRepositories +import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean +import org.springframework.data.repository.config.BootstrapMode +import org.springframework.data.repository.config.DefaultRepositoryBaseClass +import org.springframework.data.repository.query.QueryLookupStrategy +import repo.config.JpaConfig +import kotlin.reflect.KClass + +/** + * org.springframework.data.jpa.repository.config.EnableJpaRepositories를 대체하는 애노테이션 + * transactionManagerRef와 entityManagerFactoryRef는 JpaConfig에서 설정한 값을 기본으로 사용한다. + * @see EnableJpaRepositories + */ +@Target(AnnotationTarget.CLASS) +@Retention(AnnotationRetention.RUNTIME) +@EnableJpaRepositories( + transactionManagerRef = JpaConfig.JPA_TX, + entityManagerFactoryRef = JpaConfig.JPA_EMF, +) +annotation class EnableJpaRepositories( + @get:AliasFor(annotation = EnableJpaRepositories::class, attribute = "value") + val value: Array = [], + @get:AliasFor(annotation = EnableJpaRepositories::class, attribute = "basePackages") + val basePackages: Array = [], + @get:AliasFor(annotation = EnableJpaRepositories::class, attribute = "includeFilters") + val includeFilters: Array = [], + @get:AliasFor(annotation = EnableJpaRepositories::class, attribute = "excludeFilters") + val excludeFilters: Array = [], + @get:AliasFor(annotation = EnableJpaRepositories::class, attribute = "repositoryImplementationPostfix") + val repositoryImplementationPostfix: String = "Impl", + @get:AliasFor(annotation = EnableJpaRepositories::class, attribute = "namedQueriesLocation") + val namedQueriesLocation: String = "", + @get:AliasFor(annotation = EnableJpaRepositories::class, attribute = "queryLookupStrategy") + val queryLookupStrategy: QueryLookupStrategy.Key = QueryLookupStrategy.Key.CREATE_IF_NOT_FOUND, + @get:AliasFor(annotation = EnableJpaRepositories::class, attribute = "repositoryFactoryBeanClass") + val repositoryFactoryBeanClass: KClass<*> = JpaRepositoryFactoryBean::class, + @get:AliasFor(annotation = EnableJpaRepositories::class, attribute = "repositoryBaseClass") + val repositoryBaseClass: KClass<*> = DefaultRepositoryBaseClass::class, + @get:AliasFor(annotation = EnableJpaRepositories::class, attribute = "considerNestedRepositories") + val considerNestedRepositories: Boolean = false, + @get:AliasFor(annotation = EnableJpaRepositories::class, attribute = "enableDefaultTransactions") + val enableDefaultTransactions: Boolean = true, + @get:AliasFor(annotation = EnableJpaRepositories::class, attribute = "bootstrapMode") + val bootstrapMode: BootstrapMode = BootstrapMode.DEFAULT, + @get:AliasFor(annotation = EnableJpaRepositories::class, attribute = "escapeCharacter") + val escapeCharacter: Char = '\\', +) \ No newline at end of file diff --git a/repo/src/main/kotlin/repo/jpa/EntityTimeJsonFormat.kt b/repo/src/main/kotlin/repo/jpa/EntityTimeJsonFormat.kt new file mode 100644 index 00000000..b029ff1f --- /dev/null +++ b/repo/src/main/kotlin/repo/jpa/EntityTimeJsonFormat.kt @@ -0,0 +1,20 @@ +package repo.jpa + +import com.fasterxml.jackson.annotation.JsonFormat + +@MustBeDocumented +@Target( + AnnotationTarget.ANNOTATION_CLASS, + AnnotationTarget.FIELD, + AnnotationTarget.FUNCTION, + AnnotationTarget.VALUE_PARAMETER, + AnnotationTarget.CLASS, +) +@Retention(AnnotationRetention.RUNTIME) +@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = EntityTimeJsonFormat.TIME_FORMAT, timezone = EntityTimeJsonFormat.TIME_ZONE) +annotation class EntityTimeJsonFormat { + companion object { + const val TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" + const val TIME_ZONE = "Asia/Seoul" + } +} \ No newline at end of file diff --git a/repo/src/main/kotlin/repo/jpa/JpaTransactional.kt b/repo/src/main/kotlin/repo/jpa/JpaTransactional.kt new file mode 100644 index 00000000..abbd6152 --- /dev/null +++ b/repo/src/main/kotlin/repo/jpa/JpaTransactional.kt @@ -0,0 +1,38 @@ +package repo.jpa + +import org.springframework.core.annotation.AliasFor +import org.springframework.transaction.annotation.Isolation +import org.springframework.transaction.annotation.Propagation +import org.springframework.transaction.annotation.Transactional +import repo.config.JpaConfig.Companion.JPA_TX +import kotlin.reflect.KClass + +/** + * JPA 트랜잭션을 처리하는 어노테이션 + * transactionManager는 JpaConfig에서 설정한 값을 기본으로 사용한다. + */ +@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.RUNTIME) +@Transactional(transactionManager = JPA_TX) +annotation class JpaTransactional( + @get:AliasFor(annotation = Transactional::class, attribute = "label") + val label: Array = [], + @get:AliasFor(annotation = Transactional::class, attribute = "propagation") + val propagation: Propagation = Propagation.REQUIRED, + @get:AliasFor(annotation = Transactional::class, attribute = "isolation") + val isolation: Isolation = Isolation.DEFAULT, + @get:AliasFor(annotation = Transactional::class, attribute = "timeout") + val timeout: Int = -1, + @get:AliasFor(annotation = Transactional::class, attribute = "timeoutString") + val timeoutString: String = "", + @get:AliasFor(annotation = Transactional::class, attribute = "readOnly") + val readOnly: Boolean = false, + @get:AliasFor(annotation = Transactional::class, attribute = "rollbackFor") + val rollbackFor: Array> = [], + @get:AliasFor(annotation = Transactional::class, attribute = "rollbackForClassName") + val rollbackForClassName: Array = [], + @get:AliasFor(annotation = Transactional::class, attribute = "noRollbackFor") + val noRollbackFor: Array> = [], + @get:AliasFor(annotation = Transactional::class, attribute = "noRollbackForClassName") + val noRollbackForClassName: Array = [], +) \ No newline at end of file From 585820b68c380de5363b6ae6b4501a41c4eed32a Mon Sep 17 00:00:00 2001 From: belljun3395 <195850@jnu.ac.kr> Date: Wed, 18 Dec 2024 23:15:31 +0900 Subject: [PATCH 10/15] =?UTF-8?q?feat:=20ApiJpaConfig=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/src/main/kotlin/com/few/api/config/ApiJpaConfig.kt | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 api/src/main/kotlin/com/few/api/config/ApiJpaConfig.kt diff --git a/api/src/main/kotlin/com/few/api/config/ApiJpaConfig.kt b/api/src/main/kotlin/com/few/api/config/ApiJpaConfig.kt new file mode 100644 index 00000000..47707d8e --- /dev/null +++ b/api/src/main/kotlin/com/few/api/config/ApiJpaConfig.kt @@ -0,0 +1,8 @@ +package com.few.api.config + +import org.springframework.context.annotation.Configuration +import repo.jpa.EnableJpaRepositories + +@Configuration +@EnableJpaRepositories(basePackages = [ApiConfig.BASE_PACKAGE]) +class ApiJpaConfig \ No newline at end of file From 5b02e9631638ce46588722491181bf235156d7be Mon Sep 17 00:00:00 2001 From: belljun3395 <195850@jnu.ac.kr> Date: Wed, 18 Dec 2024 23:16:52 +0900 Subject: [PATCH 11/15] =?UTF-8?q?refactor:=20V2=EC=97=90=EB=8A=94=20domain?= =?UTF-8?q?=EC=97=90=20=EB=94=B0=EB=9D=BC=20dataMigration=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=ED=95=84=ED=84=B0=EB=A7=81=20=ED=95=A0=20=EC=88=98?= =?UTF-8?q?=20=EC=9E=88=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 237d21e8..29e48061 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -256,10 +256,32 @@ subprojects { val dataMigrationDir = "$root/data/$flyWayResourceDir" File(dataMigrationDir).walkTopDown().forEach { if (it.isFile) { - it.copyTo( - File("${project.projectDir}/src/main/resources$flyWayResourceDir/${it.name}"), - true, - ) + // Migration is executed in Api module. So, copy the migration file to the Api module. + if (project.name == "api") { + it.copyTo( + File("${project.projectDir}/src/main/resources$flyWayResourceDir/${it.name}"), + true, + ) + println("Copy ${it.name} to ${project.projectDir}/src/main/resources$flyWayResourceDir/${it.name}") + // If domain module, Copy domain necessary migration files. + } else if (project.name.contains("domain")) { + // Copy all V1 migration files. + if (it.name.startsWith("V1.")) { + it.copyTo( + File("${project.projectDir}/src/main/resources$flyWayResourceDir/${it.name}"), + true, + ) + // Copy domain specific migration files. + } else if (it.name.startsWith("V2.")) { + if (it.name.contains("__${project.name}__")) { + it.copyTo( + File("${project.projectDir}/src/main/resources$flyWayResourceDir/${it.name}"), + true, + ) + } + } + println("Copy ${it.name} to ${project.projectDir}/src/main/resources$flyWayResourceDir/${it.name}") + } } } } @@ -339,7 +361,11 @@ subprojects { /** do all jooq codegen */ tasks.register("jooqCodegenAll") { - dependsOn("copyDataMigrationAll") + /** copy data migration */ + subprojects.forEach { + dependsOn(it.tasks.named("copyDataMigration")) + } + /** jooq codegen */ dependsOn(":api:jooqCodegen") } From 513d3980f5c1c6879dc96976cdd70064fbfe6553 Mon Sep 17 00:00:00 2001 From: belljun3395 <195850@jnu.ac.kr> Date: Wed, 18 Dec 2024 23:17:43 +0900 Subject: [PATCH 12/15] =?UTF-8?q?feat:=20BaseEntity=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- repo/src/main/kotlin/repo/jpa/BaseEntity.kt | 29 +++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 repo/src/main/kotlin/repo/jpa/BaseEntity.kt diff --git a/repo/src/main/kotlin/repo/jpa/BaseEntity.kt b/repo/src/main/kotlin/repo/jpa/BaseEntity.kt new file mode 100644 index 00000000..ffa02f6c --- /dev/null +++ b/repo/src/main/kotlin/repo/jpa/BaseEntity.kt @@ -0,0 +1,29 @@ +package repo.jpa + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties +import jakarta.persistence.* +import org.springframework.data.annotation.CreatedDate +import org.springframework.data.annotation.LastModifiedDate +import org.springframework.data.jpa.domain.support.AuditingEntityListener +import java.time.LocalDateTime + +@MappedSuperclass +@JsonIgnoreProperties(value = ["createdAt, modifiedAt"], allowGetters = true) +@EntityListeners(AuditingEntityListener::class) +abstract class BaseEntity { + /** 생성일 */ + @CreatedDate + @Column(name = "created_at", columnDefinition = "datetime default CURRENT_TIMESTAMP") + @EntityTimeJsonFormat + var createdAt: LocalDateTime = LocalDateTime.now() + + /** 수정일 */ + @LastModifiedDate + @Column(name = "modified_at", columnDefinition = "datetime default CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP") + @EntityTimeJsonFormat + var modifiedAt: LocalDateTime = LocalDateTime.now() + + /** 삭제 여부 */ + @Column(name = "deleted", columnDefinition = "boolean default false") + var deleted: Boolean = false +} \ No newline at end of file From ad257c01dfaf2f530539d19067c57ec0895d8d88 Mon Sep 17 00:00:00 2001 From: belljun3395 <195850@jnu.ac.kr> Date: Thu, 19 Dec 2024 21:45:04 +0900 Subject: [PATCH 13/15] =?UTF-8?q?refactor:=20TxConfig=EB=A1=9C=20Transacti?= =?UTF-8?q?onalManager=20=EC=84=A4=EC=A0=95=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/repo/config/DataSourceConfig.kt | 6 ---- repo/src/main/kotlin/repo/config/JpaConfig.kt | 9 ----- repo/src/main/kotlin/repo/config/TxConfig.kt | 36 +++++++++++++++++++ 3 files changed, 36 insertions(+), 15 deletions(-) create mode 100644 repo/src/main/kotlin/repo/config/TxConfig.kt diff --git a/repo/src/main/kotlin/repo/config/DataSourceConfig.kt b/repo/src/main/kotlin/repo/config/DataSourceConfig.kt index 78a69bce..1408f787 100644 --- a/repo/src/main/kotlin/repo/config/DataSourceConfig.kt +++ b/repo/src/main/kotlin/repo/config/DataSourceConfig.kt @@ -5,21 +5,15 @@ import org.springframework.boot.context.properties.ConfigurationProperties import org.springframework.boot.jdbc.DataSourceBuilder import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration -import org.springframework.jdbc.datasource.DataSourceTransactionManager -import org.springframework.transaction.PlatformTransactionManager import javax.sql.DataSource @Configuration class DataSourceConfig { companion object { const val DATASOURCE = RepoConfig.BEAN_NAME_PREFIX + "DataSource" - const val DATASOURCE_TX = RepoConfig.BEAN_NAME_PREFIX + "DataSourceTransactionManager" } @Bean(name = [DATASOURCE]) @ConfigurationProperties(prefix = "spring.datasource.hikari") fun dataSource(): DataSource = DataSourceBuilder.create().type(HikariDataSource::class.java).build() - - @Bean(name = [DATASOURCE_TX]) - fun transactionManager(): PlatformTransactionManager = DataSourceTransactionManager(dataSource()) } \ No newline at end of file diff --git a/repo/src/main/kotlin/repo/config/JpaConfig.kt b/repo/src/main/kotlin/repo/config/JpaConfig.kt index dd975457..49d67ac8 100644 --- a/repo/src/main/kotlin/repo/config/JpaConfig.kt +++ b/repo/src/main/kotlin/repo/config/JpaConfig.kt @@ -1,6 +1,5 @@ package repo.config -import jakarta.persistence.EntityManagerFactory import org.springframework.boot.autoconfigure.EnableAutoConfiguration import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration @@ -13,11 +12,8 @@ import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.data.jpa.repository.config.EnableJpaAuditing -import org.springframework.orm.jpa.JpaTransactionManager import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter -import org.springframework.transaction.PlatformTransactionManager -import org.springframework.transaction.annotation.EnableTransactionManagement import javax.sql.DataSource @Configuration @@ -29,12 +25,10 @@ import javax.sql.DataSource ], ) @EnableJpaAuditing -@EnableTransactionManagement class JpaConfig { companion object { const val ENTITY_UNIT = "few" const val ENTITY_PACKAGE = "com.few" - const val JPA_TX = RepoConfig.BEAN_NAME_PREFIX + "JpaTransactionManager" const val JPA_EMF = RepoConfig.BEAN_NAME_PREFIX + "JpaEntityManagerFactory" } @@ -52,9 +46,6 @@ class JpaConfig { .build() } - @Bean(name = [JPA_TX]) - fun jpaTransactionManager(emf: EntityManagerFactory): PlatformTransactionManager = JpaTransactionManager(emf) - @Bean @ConfigurationProperties(prefix = "spring.jpa") fun jpaProperties(): JpaProperties = JpaProperties() diff --git a/repo/src/main/kotlin/repo/config/TxConfig.kt b/repo/src/main/kotlin/repo/config/TxConfig.kt new file mode 100644 index 00000000..b89f461b --- /dev/null +++ b/repo/src/main/kotlin/repo/config/TxConfig.kt @@ -0,0 +1,36 @@ +package repo.config + +import jakarta.persistence.EntityManagerFactory +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.context.annotation.Import +import org.springframework.jdbc.datasource.DataSourceTransactionManager +import org.springframework.orm.jpa.JpaTransactionManager +import org.springframework.transaction.PlatformTransactionManager +import org.springframework.transaction.TransactionManager +import org.springframework.transaction.annotation.EnableTransactionManagement +import org.springframework.transaction.annotation.TransactionManagementConfigurer +import repo.config.DataSourceConfig.Companion.DATASOURCE +import javax.sql.DataSource + +@Import(JpaConfig::class, DataSourceConfig::class) +@Configuration +@EnableTransactionManagement +class TxConfig( + private val emf: EntityManagerFactory, + @Qualifier(DATASOURCE) private val dataSource: DataSource, +) : TransactionManagementConfigurer { + companion object { + const val JPA_TX = RepoConfig.BEAN_NAME_PREFIX + "JpaTransactionManager" + const val DATASOURCE_TX = RepoConfig.BEAN_NAME_PREFIX + "DataSourceTransactionManager" + } + + @Bean(name = [JPA_TX]) + fun jpaTransactionManager(): PlatformTransactionManager = JpaTransactionManager(emf) + + @Bean(name = [DATASOURCE_TX]) + fun dataSourceTransactionManager(): PlatformTransactionManager = DataSourceTransactionManager(dataSource) + + override fun annotationDrivenTransactionManager(): TransactionManager = jpaTransactionManager() +} \ No newline at end of file From c2f38062ed8326bea8054562036d0d574c3daaf5 Mon Sep 17 00:00:00 2001 From: belljun3395 <195850@jnu.ac.kr> Date: Thu, 19 Dec 2024 21:46:20 +0900 Subject: [PATCH 14/15] =?UTF-8?q?refactor:=20TxConfig=EB=A1=9C=20Transacti?= =?UTF-8?q?onalManager=20=EC=84=A4=EC=A0=95=20=EB=B6=84=EB=A6=AC=20?= =?UTF-8?q?=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- repo/src/main/kotlin/repo/config/JooqConfig.kt | 4 ++-- repo/src/main/kotlin/repo/jooq/DataSourceTransactional.kt | 2 +- repo/src/main/kotlin/repo/jpa/EnableJpaRepositories.kt | 5 +++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/repo/src/main/kotlin/repo/config/JooqConfig.kt b/repo/src/main/kotlin/repo/config/JooqConfig.kt index 42cbaba9..126171ef 100644 --- a/repo/src/main/kotlin/repo/config/JooqConfig.kt +++ b/repo/src/main/kotlin/repo/config/JooqConfig.kt @@ -13,14 +13,14 @@ import org.springframework.context.annotation.Import import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator import org.springframework.transaction.PlatformTransactionManager import repo.config.DataSourceConfig.Companion.DATASOURCE -import repo.config.DataSourceConfig.Companion.DATASOURCE_TX +import repo.config.TxConfig.Companion.DATASOURCE_TX import repo.flyway.support.ExceptionTranslator import repo.flyway.support.NativeSQLLogger import repo.flyway.support.PerformanceListener import javax.sql.DataSource @Configuration -@Import(DataSourceConfig::class) +@Import(DataSourceConfig::class, TxConfig::class) class JooqConfig( @Qualifier(DATASOURCE) private val dataSource: DataSource, @Qualifier(DATASOURCE_TX) private val txManager: PlatformTransactionManager, diff --git a/repo/src/main/kotlin/repo/jooq/DataSourceTransactional.kt b/repo/src/main/kotlin/repo/jooq/DataSourceTransactional.kt index 8cc677bf..497c3e5c 100644 --- a/repo/src/main/kotlin/repo/jooq/DataSourceTransactional.kt +++ b/repo/src/main/kotlin/repo/jooq/DataSourceTransactional.kt @@ -4,7 +4,7 @@ import org.springframework.core.annotation.AliasFor import org.springframework.transaction.annotation.Isolation import org.springframework.transaction.annotation.Propagation import org.springframework.transaction.annotation.Transactional -import repo.config.DataSourceConfig.Companion.DATASOURCE_TX +import repo.config.TxConfig.Companion.DATASOURCE_TX import kotlin.reflect.KClass /** diff --git a/repo/src/main/kotlin/repo/jpa/EnableJpaRepositories.kt b/repo/src/main/kotlin/repo/jpa/EnableJpaRepositories.kt index 57c12e10..61b919f5 100644 --- a/repo/src/main/kotlin/repo/jpa/EnableJpaRepositories.kt +++ b/repo/src/main/kotlin/repo/jpa/EnableJpaRepositories.kt @@ -8,17 +8,18 @@ import org.springframework.data.repository.config.BootstrapMode import org.springframework.data.repository.config.DefaultRepositoryBaseClass import org.springframework.data.repository.query.QueryLookupStrategy import repo.config.JpaConfig +import repo.config.TxConfig import kotlin.reflect.KClass /** * org.springframework.data.jpa.repository.config.EnableJpaRepositories를 대체하는 애노테이션 - * transactionManagerRef와 entityManagerFactoryRef는 JpaConfig에서 설정한 값을 기본으로 사용한다. + * transactionManagerRef와 entityManagerFactoryRef는 TxConfig와 JpaConfig에서 설정한 값을 기본으로 사용한다. * @see EnableJpaRepositories */ @Target(AnnotationTarget.CLASS) @Retention(AnnotationRetention.RUNTIME) @EnableJpaRepositories( - transactionManagerRef = JpaConfig.JPA_TX, + transactionManagerRef = TxConfig.JPA_TX, entityManagerFactoryRef = JpaConfig.JPA_EMF, ) annotation class EnableJpaRepositories( From b108873756ffa645cb8bb0fb8681007400eb287e Mon Sep 17 00:00:00 2001 From: belljun3395 <195850@jnu.ac.kr> Date: Thu, 19 Dec 2024 21:47:06 +0900 Subject: [PATCH 15/15] =?UTF-8?q?refactor:=20TxConfig=EC=97=90=EC=84=9C=20?= =?UTF-8?q?TransactionManagementConfigurer=EB=A5=BC=20=EA=B5=AC=ED=98=84?= =?UTF-8?q?=ED=95=98=EC=97=AC=20JpaTx=EB=A5=BC=20=EC=A7=80=EC=A0=95?= =?UTF-8?q?=ED=95=98=EC=97=AC=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/kotlin/repo/jpa/JpaTransactional.kt | 38 ------------------- 1 file changed, 38 deletions(-) delete mode 100644 repo/src/main/kotlin/repo/jpa/JpaTransactional.kt diff --git a/repo/src/main/kotlin/repo/jpa/JpaTransactional.kt b/repo/src/main/kotlin/repo/jpa/JpaTransactional.kt deleted file mode 100644 index abbd6152..00000000 --- a/repo/src/main/kotlin/repo/jpa/JpaTransactional.kt +++ /dev/null @@ -1,38 +0,0 @@ -package repo.jpa - -import org.springframework.core.annotation.AliasFor -import org.springframework.transaction.annotation.Isolation -import org.springframework.transaction.annotation.Propagation -import org.springframework.transaction.annotation.Transactional -import repo.config.JpaConfig.Companion.JPA_TX -import kotlin.reflect.KClass - -/** - * JPA 트랜잭션을 처리하는 어노테이션 - * transactionManager는 JpaConfig에서 설정한 값을 기본으로 사용한다. - */ -@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION) -@Retention(AnnotationRetention.RUNTIME) -@Transactional(transactionManager = JPA_TX) -annotation class JpaTransactional( - @get:AliasFor(annotation = Transactional::class, attribute = "label") - val label: Array = [], - @get:AliasFor(annotation = Transactional::class, attribute = "propagation") - val propagation: Propagation = Propagation.REQUIRED, - @get:AliasFor(annotation = Transactional::class, attribute = "isolation") - val isolation: Isolation = Isolation.DEFAULT, - @get:AliasFor(annotation = Transactional::class, attribute = "timeout") - val timeout: Int = -1, - @get:AliasFor(annotation = Transactional::class, attribute = "timeoutString") - val timeoutString: String = "", - @get:AliasFor(annotation = Transactional::class, attribute = "readOnly") - val readOnly: Boolean = false, - @get:AliasFor(annotation = Transactional::class, attribute = "rollbackFor") - val rollbackFor: Array> = [], - @get:AliasFor(annotation = Transactional::class, attribute = "rollbackForClassName") - val rollbackForClassName: Array = [], - @get:AliasFor(annotation = Transactional::class, attribute = "noRollbackFor") - val noRollbackFor: Array> = [], - @get:AliasFor(annotation = Transactional::class, attribute = "noRollbackForClassName") - val noRollbackForClassName: Array = [], -) \ No newline at end of file