Atomikos JTA 제거: 로컬 트랜잭션 전환 및 관련 설정 정리
- JTA/XA 미사용으로 변경: JpaTransactionManager로 전환 - Atomikos 의존성 및 설정 제거, 관련 문서 및 주석 업데이트
This commit is contained in:
@@ -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 는 조회 전용이다.
|
||||||
|
|
||||||
### 에러 처리
|
### 에러 처리
|
||||||
|
|
||||||
|
|||||||
@@ -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')
|
||||||
|
|
||||||
|
|||||||
+12
-1
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
spring:
|
spring:
|
||||||
jta:
|
|
||||||
enabled: false
|
|
||||||
config:
|
config:
|
||||||
activate:
|
activate:
|
||||||
on-profile: dev
|
on-profile: dev
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
spring:
|
spring:
|
||||||
jta:
|
|
||||||
enabled: false
|
|
||||||
config:
|
config:
|
||||||
activate:
|
activate:
|
||||||
on-profile: prod
|
on-profile: prod
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
spring:
|
spring:
|
||||||
jta:
|
|
||||||
enabled: false
|
|
||||||
web:
|
web:
|
||||||
resources:
|
resources:
|
||||||
cache:
|
cache:
|
||||||
|
|||||||
@@ -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/'
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
# application.yml ? ??
|
|
||||||
# com.atomikos.icatch.log_base_dir=/Log/eapim/portal/
|
|
||||||
# com.atomikos.icatch.log_base_name=adp_tx
|
|
||||||
@@ -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" />
|
||||||
|
|||||||
Reference in New Issue
Block a user