diff --git a/CLAUDE.md b/CLAUDE.md index c66e758..913f081 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -185,8 +185,9 @@ com.eactive.apim.portal/ 설정: `config/PortalDatasourceConfiguration.java` - 각 데이터베이스별 별도 EntityManager -- Atomikos JTA를 통한 분산 트랜잭션 -- 트랜잭션 로그: `/Log/eapim/portal/` +- **JTA/XA 미사용**. EntityManagerFactory 별 로컬 트랜잭션 (`config/PortalConfigTransaction.java`) + - `transactionManager` (@Primary) → EMS + - `gatewayTransactionManager` → Gateway ### 설정 프로파일 @@ -359,7 +360,13 @@ return queryFactory.selectFrom(user) 적절한 propagation과 함께 `@Transactional` 사용: - 기본값: `REQUIRED` (기존 트랜잭션에 참여하거나 새로 생성) - 읽기 전용 작업: 최적화를 위해 `@Transactional(readOnly = true)` -- 다중 데이터베이스: Atomikos JTA가 자동으로 분산 트랜잭션 처리 +- **다중 데이터베이스: 분산 트랜잭션(JTA/XA) 없음.** EMS·Gateway 각각 독립 로컬 트랜잭션이다. + - 무지정 `@Transactional` = EMS(`transactionManager`) + - **Gateway 엔티티(`com.eactive.apim.gateway.*`, `com.eactive.eai.data.entity.onl.*`)를 다루는 서비스는 + `@Transactional("gatewayTransactionManager")` 를 명시**한다. 안 하면 게이트웨이 EntityManager 가 + 리포지토리 호출 단위로 닫혀 지연 로딩에서 `LazyInitializationException` 이 난다 + (예: `ApiServiceService` — `ApiGroup.apiGroupApiList`). + - 두 DB 를 한 원자 단위로 묶어야 하는 작업은 만들지 않는다. 현재 Gateway 는 조회 전용이다. ### 에러 처리 diff --git a/build.gradle b/build.gradle index 3f1ad5b..59457ed 100644 --- a/build.gradle +++ b/build.gradle @@ -60,7 +60,6 @@ dependencies { // implementation project(':kjb-safedb') implementation('org.springframework.boot:spring-boot-starter') - implementation 'org.springframework.boot:spring-boot-starter-jta-atomikos' implementation('org.springframework.boot:spring-boot-starter-web') implementation('org.springframework.boot:spring-boot-starter-validation') diff --git a/src/main/java/com/eactive/apim/portal/apps/apiservice/service/ApiServiceService.java b/src/main/java/com/eactive/apim/portal/apps/apiservice/service/ApiServiceService.java index da3a0ad..3958f70 100644 --- a/src/main/java/com/eactive/apim/portal/apps/apiservice/service/ApiServiceService.java +++ b/src/main/java/com/eactive/apim/portal/apps/apiservice/service/ApiServiceService.java @@ -22,8 +22,19 @@ import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +/** + * API 그룹(전시) 조회 서비스. + * + *
주 조회 대상 {@link ApiGroup} 은 AGW(게이트웨이) 데이터소스 엔티티라 + * 트랜잭션 매니저를 {@code gatewayTransactionManager} 로 지정한다. 기본 + * {@code transactionManager}(EMS) 를 쓰면 게이트웨이 EntityManager 가 리포지토리 호출 단위로 + * 닫혀 {@code ApiGroup.apiGroupApiList} 지연 로딩에서 LazyInitializationException 이 난다.
+ * + *내부에서 호출하는 {@code ApiSpecInfoService} 는 EMS 쪽이며 자체 {@code @Transactional} + * (기본 매니저)로 독립 트랜잭션을 연다. {@code ApiSpecInfo} 에는 지연 연관이 없어 문제되지 않는다.
+ */ @Service("apiServiceService") -@Transactional +@Transactional("gatewayTransactionManager") @RequiredArgsConstructor @Slf4j public class ApiServiceService { diff --git a/src/main/java/com/eactive/apim/portal/config/BaseDatasourceConfiguration.java b/src/main/java/com/eactive/apim/portal/config/BaseDatasourceConfiguration.java index 23361ff..9304df9 100644 --- a/src/main/java/com/eactive/apim/portal/config/BaseDatasourceConfiguration.java +++ b/src/main/java/com/eactive/apim/portal/config/BaseDatasourceConfiguration.java @@ -59,11 +59,11 @@ public class BaseDatasourceConfiguration { //public interface AvailableSettings extends org.hibernate.jpa.AvailableSettings 참고 properties.put("hibernate.dialect", prop.getHibernateDialect()); properties.put("hibernate.connection.handling_mode", "DELAYED_ACQUISITION_AND_RELEASE_AFTER_TRANSACTION"); - properties.put("hibernate.transaction.jta.platform", "org.hibernate.engine.transaction.jta.platform.internal.AtomikosJtaPlatform"); properties.put("hibernate.physical_naming_strategy", prop.getHibernatePhysicalNamingStrategy()); properties.put("hibernate.default_schema", prop.getSchema()); properties.put("javax.persistence.validation.mode", "none"); - properties.put("javax.persistence.transactionType", "JTA"); + // JTA/XA 미사용. EMF 별 로컬 트랜잭션(JpaTransactionManager)으로 처리한다 - PortalConfigTransaction 참고 + properties.put("javax.persistence.transactionType", "RESOURCE_LOCAL"); properties.put("hibernate.format_sql", "true"); if (prop instanceof EmsDatasourceProperty) { diff --git a/src/main/java/com/eactive/apim/portal/config/GatewayDatasourceConfiguration.java b/src/main/java/com/eactive/apim/portal/config/GatewayDatasourceConfiguration.java index e9bc25c..938696b 100644 --- a/src/main/java/com/eactive/apim/portal/config/GatewayDatasourceConfiguration.java +++ b/src/main/java/com/eactive/apim/portal/config/GatewayDatasourceConfiguration.java @@ -18,7 +18,8 @@ import javax.sql.DataSource; @Configuration @EntityScan(basePackages = {"com.eactive.apim.gateway"}) -@EnableJpaRepositories(basePackages = "com.eactive.apim.gateway", repositoryBaseClass = BaseRepositoryImpl.class, entityManagerFactoryRef = "gatewayEntityManagerFactory") +@EnableJpaRepositories(basePackages = "com.eactive.apim.gateway", repositoryBaseClass = BaseRepositoryImpl.class, + entityManagerFactoryRef = "gatewayEntityManagerFactory", transactionManagerRef = "gatewayTransactionManager") @Slf4j public class GatewayDatasourceConfiguration extends BaseDatasourceConfiguration { private final Environment env; diff --git a/src/main/java/com/eactive/apim/portal/config/PortalConfigTransaction.java b/src/main/java/com/eactive/apim/portal/config/PortalConfigTransaction.java index 0e73ab4..2a261bd 100644 --- a/src/main/java/com/eactive/apim/portal/config/PortalConfigTransaction.java +++ b/src/main/java/com/eactive/apim/portal/config/PortalConfigTransaction.java @@ -1,39 +1,44 @@ package com.eactive.apim.portal.config; -import com.atomikos.icatch.jta.UserTransactionImp; -import com.atomikos.icatch.jta.UserTransactionManager; +import javax.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.DependsOn; +import org.springframework.context.annotation.Primary; +import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; -import org.springframework.transaction.jta.JtaTransactionManager; - -import javax.transaction.SystemException; -import javax.transaction.UserTransaction; +/** + * 트랜잭션 매니저 설정. + * + *EMS(포털)·AGW(게이트웨이) 두 데이터소스를 쓰지만 JTA/XA 는 사용하지 않는다. + * 두 DB 를 한 트랜잭션으로 묶어 쓰는 지점이 없고(게이트웨이 측은 조회 전용), + * 이전 Atomikos 구성도 {@code AtomikosDataSourceBean} 없이 JNDI/Hikari 데이터소스를 그대로 넘겨 + * XA 리소스가 하나도 enlist 되지 않는 형태였다. 즉 2PC 는 실제로 동작한 적이 없고 + * {@code com.atomikos.icatch.max_actives} 상한(기본 50)만 병목으로 남았다.
+ * + *따라서 EntityManagerFactory 별로 로컬 {@link JpaTransactionManager} 를 둔다. + * 게이트웨이 리포지토리는 {@code GatewayDatasourceConfiguration} 의 + * {@code transactionManagerRef = "gatewayTransactionManager"} 로 연결된다.
+ */ @Configuration @EnableTransactionManagement public class PortalConfigTransaction { - @Bean(name = "userTransaction") - public UserTransaction userTransaction() throws SystemException { - UserTransactionImp userTransactionImp = new UserTransactionImp(); - userTransactionImp.setTransactionTimeout(30000); - return userTransactionImp; - } - - @Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close") - public UserTransactionManager atomikosTransactionManager() { - UserTransactionManager userTransactionManager = new UserTransactionManager(); - userTransactionManager.setForceShutdown(false); - return userTransactionManager; - } - + /** EMS(포털) 기본 트랜잭션 매니저. {@code @Transactional} 무지정 시 이 매니저가 쓰인다. */ + @Primary @Bean(name = "transactionManager") - @DependsOn({"userTransaction", "atomikosTransactionManager"}) - public PlatformTransactionManager transactionManager(UserTransactionManager atomikosTransactionManager) throws SystemException { - UserTransaction userTransaction = userTransaction(); - return new JtaTransactionManager(userTransaction, atomikosTransactionManager); + public PlatformTransactionManager transactionManager( + @Qualifier("entityManagerFactory") EntityManagerFactory entityManagerFactory) { + return new JpaTransactionManager(entityManagerFactory); + } + + /** AGW(게이트웨이) 트랜잭션 매니저. {@code @Transactional("gatewayTransactionManager")} 로 지정해 쓴다. */ + @Bean(name = "gatewayTransactionManager") + public PlatformTransactionManager gatewayTransactionManager( + @Qualifier("gatewayEntityManagerFactory") EntityManagerFactory gatewayEntityManagerFactory) { + return new JpaTransactionManager(gatewayEntityManagerFactory); } } diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 24b95e2..1627020 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -1,6 +1,4 @@ spring: - jta: - enabled: false config: activate: on-profile: dev diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index cc5d35e..c4dbce4 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -1,6 +1,4 @@ spring: - jta: - enabled: false config: activate: on-profile: prod diff --git a/src/main/resources/application-stage.yml b/src/main/resources/application-stage.yml index 86f638c..ff3de3b 100644 --- a/src/main/resources/application-stage.yml +++ b/src/main/resources/application-stage.yml @@ -1,6 +1,4 @@ spring: - jta: - enabled: false web: resources: cache: diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index fef0fab..3175dad 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -58,14 +58,7 @@ spring: # 설정으로는 적용되지 않는다. 실제 버전닝은 PortalConfigWebDispatcherServlet 가 # 아래 app.resource-versioning.enabled 토글을 읽어 직접 수행한다. - jta: - enabled: false - atomikos: - properties: -# log-base-dir: /logs/eapim/${inst.Name:devSvr11}/atomikos - log-base-dir: /dev/null - log-base-name: adp_tx - max-actives: 200 + # JTA/Atomikos 제거됨. EMS·AGW 각각 로컬 트랜잭션(JpaTransactionManager) 사용 - PortalConfigTransaction 참고 thymeleaf: prefix: 'classpath:/templates/views/' diff --git a/src/main/resources/jta.properties b/src/main/resources/jta.properties deleted file mode 100644 index bd73bc5..0000000 --- a/src/main/resources/jta.properties +++ /dev/null @@ -1,3 +0,0 @@ -# application.yml ? ?? -# com.atomikos.icatch.log_base_dir=/Log/eapim/portal/ -# com.atomikos.icatch.log_base_name=adp_tx diff --git a/src/main/resources/logback-debug.xml b/src/main/resources/logback-debug.xml index d905718..b3137d2 100644 --- a/src/main/resources/logback-debug.xml +++ b/src/main/resources/logback-debug.xml @@ -87,9 +87,6 @@