Atomikos JTA 제거: 로컬 트랜잭션 전환 및 관련 설정 정리
eapim-portal CI / build (push) Has been cancelled
eapim-portal Test / test (push) Has been cancelled

- JTA/XA 미사용으로 변경: JpaTransactionManager로 전환
- Atomikos 의존성 및 설정 제거, 관련 문서 및 주석 업데이트
This commit is contained in:
Rinjae
2026-08-14 17:11:54 +09:00
parent 7a0f88bfb1
commit 3cfd6b0b04
12 changed files with 57 additions and 53 deletions
+10 -3
View File
@@ -185,8 +185,9 @@ com.eactive.apim.portal/
설정: `config/PortalDatasourceConfiguration.java` 설정: `config/PortalDatasourceConfiguration.java`
- 각 데이터베이스별 별도 EntityManager - 각 데이터베이스별 별도 EntityManager
- Atomikos JTA를 통한 분산 트랜잭션 - **JTA/XA 미사용**. EntityManagerFactory 별 로컬 트랜잭션 (`config/PortalConfigTransaction.java`)
- 트랜잭션 로그: `/Log/eapim/portal/` - `transactionManager` (@Primary) → EMS
- `gatewayTransactionManager` → Gateway
### 설정 프로파일 ### 설정 프로파일
@@ -359,7 +360,13 @@ return queryFactory.selectFrom(user)
적절한 propagation과 함께 `@Transactional` 사용: 적절한 propagation과 함께 `@Transactional` 사용:
- 기본값: `REQUIRED` (기존 트랜잭션에 참여하거나 새로 생성) - 기본값: `REQUIRED` (기존 트랜잭션에 참여하거나 새로 생성)
- 읽기 전용 작업: 최적화를 위해 `@Transactional(readOnly = true)` - 읽기 전용 작업: 최적화를 위해 `@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 는 조회 전용이다.
### 에러 처리 ### 에러 처리
-1
View File
@@ -60,7 +60,6 @@ dependencies {
// implementation project(':kjb-safedb') // implementation project(':kjb-safedb')
implementation('org.springframework.boot:spring-boot-starter') 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-web')
implementation('org.springframework.boot:spring-boot-starter-validation') implementation('org.springframework.boot:spring-boot-starter-validation')
@@ -22,8 +22,19 @@ import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
/**
* API 그룹(전시) 조회 서비스.
*
* <p>주 조회 대상 {@link ApiGroup} 은 <b>AGW(게이트웨이) 데이터소스</b> 엔티티라
* 트랜잭션 매니저를 {@code gatewayTransactionManager} 로 지정한다. 기본
* {@code transactionManager}(EMS) 를 쓰면 게이트웨이 EntityManager 가 리포지토리 호출 단위로
* 닫혀 {@code ApiGroup.apiGroupApiList} 지연 로딩에서 LazyInitializationException 이 난다.</p>
*
* <p>내부에서 호출하는 {@code ApiSpecInfoService} 는 EMS 쪽이며 자체 {@code @Transactional}
* (기본 매니저)로 독립 트랜잭션을 연다. {@code ApiSpecInfo} 에는 지연 연관이 없어 문제되지 않는다.</p>
*/
@Service("apiServiceService") @Service("apiServiceService")
@Transactional @Transactional("gatewayTransactionManager")
@RequiredArgsConstructor @RequiredArgsConstructor
@Slf4j @Slf4j
public class ApiServiceService { public class ApiServiceService {
@@ -59,11 +59,11 @@ public class BaseDatasourceConfiguration {
//public interface AvailableSettings extends org.hibernate.jpa.AvailableSettings 참고 //public interface AvailableSettings extends org.hibernate.jpa.AvailableSettings 참고
properties.put("hibernate.dialect", prop.getHibernateDialect()); properties.put("hibernate.dialect", prop.getHibernateDialect());
properties.put("hibernate.connection.handling_mode", "DELAYED_ACQUISITION_AND_RELEASE_AFTER_TRANSACTION"); 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.physical_naming_strategy", prop.getHibernatePhysicalNamingStrategy());
properties.put("hibernate.default_schema", prop.getSchema()); properties.put("hibernate.default_schema", prop.getSchema());
properties.put("javax.persistence.validation.mode", "none"); 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"); properties.put("hibernate.format_sql", "true");
if (prop instanceof EmsDatasourceProperty) { if (prop instanceof EmsDatasourceProperty) {
@@ -18,7 +18,8 @@ import javax.sql.DataSource;
@Configuration @Configuration
@EntityScan(basePackages = {"com.eactive.apim.gateway"}) @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 @Slf4j
public class GatewayDatasourceConfiguration extends BaseDatasourceConfiguration { public class GatewayDatasourceConfiguration extends BaseDatasourceConfiguration {
private final Environment env; private final Environment env;
@@ -1,39 +1,44 @@
package com.eactive.apim.portal.config; package com.eactive.apim.portal.config;
import com.atomikos.icatch.jta.UserTransactionImp; import javax.persistence.EntityManagerFactory;
import com.atomikos.icatch.jta.UserTransactionManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; 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.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.jta.JtaTransactionManager;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;
/**
* 트랜잭션 매니저 설정.
*
* <p>EMS(포털)·AGW(게이트웨이) 두 데이터소스를 쓰지만 <b>JTA/XA 는 사용하지 않는다</b>.
* 두 DB 를 한 트랜잭션으로 묶어 쓰는 지점이 없고(게이트웨이 측은 조회 전용),
* 이전 Atomikos 구성도 {@code AtomikosDataSourceBean} 없이 JNDI/Hikari 데이터소스를 그대로 넘겨
* XA 리소스가 하나도 enlist 되지 않는 형태였다. 즉 2PC 는 실제로 동작한 적이 없고
* {@code com.atomikos.icatch.max_actives} 상한(기본 50)만 병목으로 남았다.</p>
*
* <p>따라서 EntityManagerFactory 별로 로컬 {@link JpaTransactionManager} 를 둔다.
* 게이트웨이 리포지토리는 {@code GatewayDatasourceConfiguration} 의
* {@code transactionManagerRef = "gatewayTransactionManager"} 로 연결된다.</p>
*/
@Configuration @Configuration
@EnableTransactionManagement @EnableTransactionManagement
public class PortalConfigTransaction { public class PortalConfigTransaction {
@Bean(name = "userTransaction") /** EMS(포털) 기본 트랜잭션 매니저. {@code @Transactional} 무지정 시 이 매니저가 쓰인다. */
public UserTransaction userTransaction() throws SystemException { @Primary
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;
}
@Bean(name = "transactionManager") @Bean(name = "transactionManager")
@DependsOn({"userTransaction", "atomikosTransactionManager"}) public PlatformTransactionManager transactionManager(
public PlatformTransactionManager transactionManager(UserTransactionManager atomikosTransactionManager) throws SystemException { @Qualifier("entityManagerFactory") EntityManagerFactory entityManagerFactory) {
UserTransaction userTransaction = userTransaction(); return new JpaTransactionManager(entityManagerFactory);
return new JtaTransactionManager(userTransaction, atomikosTransactionManager); }
/** AGW(게이트웨이) 트랜잭션 매니저. {@code @Transactional("gatewayTransactionManager")} 로 지정해 쓴다. */
@Bean(name = "gatewayTransactionManager")
public PlatformTransactionManager gatewayTransactionManager(
@Qualifier("gatewayEntityManagerFactory") EntityManagerFactory gatewayEntityManagerFactory) {
return new JpaTransactionManager(gatewayEntityManagerFactory);
} }
} }
-2
View File
@@ -1,6 +1,4 @@
spring: spring:
jta:
enabled: false
config: config:
activate: activate:
on-profile: dev on-profile: dev
-2
View File
@@ -1,6 +1,4 @@
spring: spring:
jta:
enabled: false
config: config:
activate: activate:
on-profile: prod on-profile: prod
-2
View File
@@ -1,6 +1,4 @@
spring: spring:
jta:
enabled: false
web: web:
resources: resources:
cache: cache:
+1 -8
View File
@@ -58,14 +58,7 @@ spring:
# 설정으로는 적용되지 않는다. 실제 버전닝은 PortalConfigWebDispatcherServlet 가 # 설정으로는 적용되지 않는다. 실제 버전닝은 PortalConfigWebDispatcherServlet 가
# 아래 app.resource-versioning.enabled 토글을 읽어 직접 수행한다. # 아래 app.resource-versioning.enabled 토글을 읽어 직접 수행한다.
jta: # JTA/Atomikos 제거됨. EMS·AGW 각각 로컬 트랜잭션(JpaTransactionManager) 사용 - PortalConfigTransaction 참고
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
thymeleaf: thymeleaf:
prefix: 'classpath:/templates/views/' prefix: 'classpath:/templates/views/'
-3
View File
@@ -1,3 +0,0 @@
# application.yml ? ??
# com.atomikos.icatch.log_base_dir=/Log/eapim/portal/
# com.atomikos.icatch.log_base_name=adp_tx
-3
View File
@@ -87,9 +87,6 @@
<logger name="org.springframework.orm.jpa" level="DEBUG" additivity="false"> <logger name="org.springframework.orm.jpa" level="DEBUG" additivity="false">
<appender-ref ref="FILE_HIBERNATE" /> <appender-ref ref="FILE_HIBERNATE" />
</logger> </logger>
<logger name="com.atomikos" level="DEBUG" additivity="false">
<appender-ref ref="FILE_HIBERNATE" />
</logger>
<logger name="eapim.portal.session" level="INFO" additivity="false"> <logger name="eapim.portal.session" level="INFO" additivity="false">
<appender-ref ref="HTTP_SESSION" /> <appender-ref ref="HTTP_SESSION" />