@Transactional 애너테이션을 적용해도 매번 session을 맺어서 처리하는 이유
원인을 찾았습니다. 데이터소스를 여러개 설정해 놓으면 @Transactional이 안 먹는 현상이었습니다. ( 리모트db 테스트와 jndi 테스트때문에 여러개 만듬..;;;)@Bean(name = "FirstDatasource") @Profile("!jndi") @ConfigurationProperties("spring.datasource.nxmdb") public DataSource firstDatasource() { log.info("firstDatasource====="); return DataSourceBuilder.create() .type(HikariDataSource.class) .build(); } ---> 추가해야함. @Bean(name = "transactionManager") @Primary public PlatformTransactionManager transactionManager() { DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(); transactionManager.setDataSource(firstDatasource()); return transactionManager; }트랜잭션 매니저를 생성해주니 잘 동작합니다.------------------------------------------------------------ 리모트 db 트랜잭션 org.springframework.boot spring-boot-starter-jta-atomikos 2.7.12 oracle xa설정 sqlplus sys as sysdba grant select on sys.dba_pending_transactions to anytest; grant select on sys.pending_trans$ to anytest; grant select on sys.dba_2pc_pending to anytest; grant execute on sys.dbms_system to anytest; grant execute on dbms_xa to anytest; GRANT EXECUTE ON sys.dbms_system TO system; GRANT SELECT ON sys.dba_pending_transactions TO system; GRANT SELECT ON sys.dba_2pc_pending TO system; GRANT SELECT ON sys.pending_trans$ TO system; GRANT SELECT ON sys.dba_pending_transactions TO system;application-local.yml spring: config: activate: on-profile: local jta: enabled: true nxmdb : datasource: jdbc-url: jdbc:oracle:thin:@localhost:1521/xe username: system password: root driverClassName: oracle.jdbc.OracleDriver anytest : datasource: jdbc-url: jdbc:oracle:thin:@localhost:1521/xe username: anytest password: nxcus2023 driverClassName: oracle.jdbc.OracleDriver db1: datasource: xa-data-source-class-name: oracle.jdbc.xa.client.OracleXADataSource xa-properties: user: system password: root url: jdbc:oracle:thin:@localhost:1521/xe db2: datasource: xa-data-source-class-name: oracle.jdbc.xa.client.OracleXADataSource xa-properties: user: anytest password: nxcus2023 url: jdbc:oracle:thin:@localhost:1521/xe server: port: 8808package com.skcc.nxcus_spring.config; import com.atomikos.icatch.jta.UserTransactionImp; import com.atomikos.icatch.jta.UserTransactionManager; import com.zaxxer.hikari.HikariDataSource; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionTemplate; import org.mybatis.spring.annotation.MapperScan; import org.mybatis.spring.boot.autoconfigure.SpringBootVFS; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean; import org.springframework.context.annotation.*; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.jta.JtaTransactionManager; import javax.sql.DataSource; import javax.transaction.TransactionManager; import javax.transaction.UserTransaction; import java.util.Properties; @Slf4j @Configuration @EnableTransactionManagement @MapperScan(value="com.skcc.nxcus_spring.mapper.nxmdb", sqlSessionFactoryRef="nxmdbSessionFactory") public class FirstDatasourceConfig { /* @Bean(name = "firstDatasource") @Profile("!jndi") @ConfigurationProperties(prefix = "spring.nxmdb.datasource") public DataSource firstDatasource() { log.info("originalDataSource====="); return DataSourceBuilder.create() .type(HikariDataSource.class) .build(); } @Bean(name = "firstDatasource") @Profile("jndi") public DataSource jndifirstDatasource() { log.info("jndifirstDatasource====="); JndiDataSourceLookup jndiDataSourceLookup = new JndiDataSourceLookup(); return jndiDataSourceLookup.getDataSource("OCBX01"); } / @Bean(name = "nxmdbSessionFactory") public SqlSessionFactory nxmdbSessionFactory(@Qualifier("firstDatasource")DataSource dataSource) throws Exception { log.info("nxmdbSessionFactory====="); SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); factoryBean.setDataSource(dataSource); factoryBean.setVfs(SpringBootVFS.class); ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); Resource[] resource = resolver.getResources("mapper/.xml"); factoryBean.setMapperLocations(resource); factoryBean.setTypeAliasesPackage("com.skcc.nxcus_spring"); return factoryBean.getObject(); } @Bean(name = "nxmdbSqlSessionTemplate") public SqlSessionTemplate nxmdbSessionTemplate(SqlSessionFactory nxmdbSessionFactory) { log.info("nxmdbSessionTemplate====="); return new SqlSessionTemplate(nxmdbSessionFactory); } /* @Bean(name = "nxmdbTransactionManager") public PlatformTransactionManager nxmdbTransactionManager(@Qualifier("firstDatasource") DataSource dataSource) { log.info("nxmdbTransactionManager====="); DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(); transactionManager.setDataSource(dataSource); return transactionManager; } */ ////////////////////////////////////////////////////////////////////// @Value("${spring.db1.datasource.xa-data-source-class-name}") String ds1XaDataSourceClassName; @Value("${spring.db1.datasource.xa-properties.user}") String ds1User; @Value("${spring.db1.datasource.xa-properties.password}") String ds1Password; @Value("${spring.db1.datasource.xa-properties.url}") String ds1Url; @Bean(name = "firstDatasource") public DataSource dataSource() { log.info("db1DataSource AtomikosDataSourceBean"); AtomikosDataSourceBean ds = new AtomikosDataSourceBean(); ds.setUniqueResourceName(DS1_DATASOURCE); ds.setXaDataSourceClassName(ds1XaDataSourceClassName); Properties p = new Properties(); p.setProperty("user", ds1User); p.setProperty("password", ds1Password); p.setProperty("URL", ds1Url); ds.setXaProperties (p); return ds; } @Bean(name = "userTransaction") public UserTransaction userTransaction() throws Throwable { UserTransactionImp userTransactionImp = new UserTransactionImp(); userTransactionImp.setTransactionTimeout(10000); return userTransactionImp; } @Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close") public TransactionManager atomikosTransactionManager() throws Throwable { UserTransactionManager userTransactionManager = new UserTransactionManager(); userTransactionManager.setForceShutdown(false); return userTransactionManager; } @Bean(name = "multiTxManager") @DependsOn({ "userTransaction", "atomikosTransactionManager" }) public PlatformTransactionManager transactionManager() throws Throwable { log.info("multiTxManager=PlatformTransactionManager==transactionManager=="); UserTransaction userTransaction = userTransaction(); JtaTransactionManager manager = new JtaTransactionManager(userTransaction, atomikosTransactionManager()); return manager; } }package com.skcc.nxcus_spring.config; import com.zaxxer.hikari.HikariDataSource; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionTemplate; import org.mybatis.spring.annotation.MapperScan; import org.mybatis.spring.boot.autoconfigure.SpringBootVFS; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.DependsOn; import org.springframework.context.annotation.Profile; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup; import org.springframework.transaction.PlatformTransactionManager; import javax.sql.DataSource; import java.util.Properties; @Slf4j @Configuration @MapperScan(value="com.skcc.nxcus_spring.mapper.anytest", sqlSessionFactoryRef="anytestSessionFactory") public class SecondDatasourceConfig { /* @Bean(name = "secondDatasource") @Profile("!jndi") @ConfigurationProperties("spring.anytest.datasource") public DataSource secondDatasource() { log.info("local or server"); return DataSourceBuilder.create() .type(HikariDataSource.class) .build(); } @Bean(name = "secondDatasource") @Profile("jndi") public DataSource secoundjndiDatasource() { JndiDataSourceLookup jndiDataSourceLookup = new JndiDataSourceLookup(); return jndiDataSourceLookup.getDataSource("OCBX"); } / @Bean(name = "anytestSessionFactory") public SqlSessionFactory anytestSessionFactory(@Qualifier("secondDatasource")DataSource ds) throws Exception { SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); factoryBean.setDataSource(ds); factoryBean.setVfs(SpringBootVFS.class); ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); Resource[] resource = resolver.getResources("mapper/.xml"); factoryBean.setMapperLocations(resource); factoryBean.setTypeAliasesPackage("com.skcc.nxcus_spring"); return factoryBean.getObject(); } @Bean(name = "anytestSqlSessionTemplate") public SqlSessionTemplate anytestSqlSessionTemplate(SqlSessionFactory anytestSessionFactory) { return new SqlSessionTemplate(anytestSessionFactory); } /* @Bean(name = "fepdbTransactionManager") public PlatformTransactionManager remoteTransactionManager(@Qualifier("secondDatasource") DataSource dataSource) { DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(); transactionManager.setDataSource(dataSource); return transactionManager; } */ ////////////////////////////////////// public static final String DS2_DATASOURCE = "secondDatasource"; @Value("${spring.db2.datasource.xa-data-source-class-name}") String ds2XaDataSourceClassName; @Value("${spring.db2.datasource.xa-properties.user}") String ds2User; @Value("${spring.db2.datasource.xa-properties.password}") String ds2Password; @Value("${spring.db2.datasource.xa-properties.url}") String ds2Url; @Bean(name = "secondDatasource") public DataSource dataSource() { log.info("db2DataSource AtomikosDataSourceBean"); AtomikosDataSourceBean ds = new AtomikosDataSourceBean(); ds.setUniqueResourceName(DS2_DATASOURCE); ds.setXaDataSourceClassName(ds2XaDataSourceClassName); Properties p = new Properties(); p.setProperty("user", ds2User); p.setProperty("password", ds2Password); p.setProperty("URL", ds2Url); ds.setXaProperties (p); return ds; } }@Transactional(rollbackFor = Exception.class, value = "multiTxManager") public int testTran(DatasetList dsList) { Dataset dsParam = DatasetUtil.getGdsParam(dsList); // Dataset을 SQL에 적용할 변수형인 Map으로 변환하면서 사용자 정보 입력 Map mapParam = DatasetUtil.getMap(dsParam, DatasetUtil.UPPER_CASE); log.info("aop class={}", codeMapper.getClass()); //트랜잭션이 활성화되었는지 확인 boolean txActive = TransactionSynchronizationManager.isActualTransactionActive(); log.info("tx active={}",txActive); int iRes1 = 0; iRes1 = codeMapper.updateTran1(mapParam); iRes1 = fepMapper.updateFep(mapParam); //리모트디비 iRes1 = codeMapper.updateTran2(mapParam); return iRes1; } 작성하니 세션도 db별로 1개씩 총 2개 생성되고 롤백도 잘 되네요.