From 7babf9653ead590c2eb62f18bd0678e2ffd3e6f8 Mon Sep 17 00:00:00 2001 From: "semin.baek" Date: Wed, 26 Feb 2025 16:51:57 +0900 Subject: [PATCH] =?UTF-8?q?=EB=8F=99=EC=A0=81=20=EB=8B=A4=EC=A4=91=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=EC=86=8C=EC=8A=A4,=20sqlSession=20?= =?UTF-8?q?=EC=9E=91=EC=97=85=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 6 ++ .../agent/config/DfxAgentConfiguration.java | 55 +++++++++++-- .../agent/config/DynamicDataSourceConfig.java | 57 ------------- .../config/DynamicDataSourceService.java | 28 ------- .../com/bsmlab/dfx/agent/config/Settings.java | 6 +- .../bsmlab/dfx/agent/config/UserMapper.java | 12 --- .../bsmlab/dfx/agent/config/UserService.java | 26 ------ .../config/datasource/DataSourcePool.java | 1 + .../datasource/DynamicDataSourceService.java | 32 ++++++++ .../DynamicRoutingDataSource.java | 23 +++--- .../RefreshableSqlSessionFactoryBean.java | 2 - .../config/datasource/SqlExecuteService.java | 79 +++++++++++++++++++ 12 files changed, 187 insertions(+), 140 deletions(-) delete mode 100644 src/main/java/com/bsmlab/dfx/agent/config/DynamicDataSourceConfig.java delete mode 100644 src/main/java/com/bsmlab/dfx/agent/config/DynamicDataSourceService.java delete mode 100644 src/main/java/com/bsmlab/dfx/agent/config/UserMapper.java delete mode 100644 src/main/java/com/bsmlab/dfx/agent/config/UserService.java create mode 100644 src/main/java/com/bsmlab/dfx/agent/config/datasource/DynamicDataSourceService.java rename src/main/java/com/bsmlab/dfx/agent/config/{ => datasource}/DynamicRoutingDataSource.java (50%) create mode 100644 src/main/java/com/bsmlab/dfx/agent/config/datasource/SqlExecuteService.java diff --git a/build.gradle b/build.gradle index d1b712c..3ce8858 100644 --- a/build.gradle +++ b/build.gradle @@ -38,6 +38,12 @@ dependencies { testImplementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter-test:3.0.4' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.17.0' + implementation group: 'org.apache.commons', name: 'commons-dbcp2', version: '2.12.0' + implementation group: 'org.postgresql', name: 'postgresql', version: '42.7.4' + implementation group: 'org.mariadb.jdbc', name: 'mariadb-java-client', version: '3.5.2' + implementation group: 'com.microsoft.sqlserver', name: 'mssql-jdbc', version: '12.8.1.jre8' + implementation group: 'com.ibm.db2', name: 'jcc', version: '12.1.0.0' + } tasks.named('test') { diff --git a/src/main/java/com/bsmlab/dfx/agent/config/DfxAgentConfiguration.java b/src/main/java/com/bsmlab/dfx/agent/config/DfxAgentConfiguration.java index e2765ed..d642b42 100644 --- a/src/main/java/com/bsmlab/dfx/agent/config/DfxAgentConfiguration.java +++ b/src/main/java/com/bsmlab/dfx/agent/config/DfxAgentConfiguration.java @@ -1,21 +1,29 @@ package com.bsmlab.dfx.agent.config; +import com.bsmlab.dfx.agent.config.datasource.DataSourceDto; +import com.bsmlab.dfx.agent.config.datasource.DynamicRoutingDataSource; import com.bsmlab.dfx.agent.config.datasource.RefreshableSqlSessionFactoryBean; import io.micrometer.common.util.StringUtils; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.dbcp2.BasicDataSource; import org.mybatis.spring.SqlSessionFactoryBean; +import org.mybatis.spring.annotation.MapperScan; +import org.mybatis.spring.annotation.MapperScans; import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.Resource; +import java.util.Map; + @Slf4j @Configuration @RequiredArgsConstructor +@MapperScans({ + @MapperScan("com.bsmlab.dfx.agent") +}) public class DfxAgentConfiguration { - public final ApplicationContext applicationContext; // java -jar dfxagent.jar --embedded.db.file.directory=D:\projects\bsm-lab\dfx\dfxagent\src\docs\settings-examples --setting.file=D:\projects\bsm-lab\dfx\dfxagent\src\docs\settings-examples\dfxagent.json // embedded.db.file.directory=D:\projects\bsm-lab\dfx\dfxagent\src\docs\settings-examples @@ -43,12 +51,49 @@ public class DfxAgentConfiguration { } @Bean - public SqlSessionFactoryBean sqlSessionFactoryBean () { + public SqlSessionFactoryBean sqlSessionFactoryBean(Settings settings) { RefreshableSqlSessionFactoryBean sqlSessionFactoryBean = new RefreshableSqlSessionFactoryBean(); - //setMapperLocations(Resource[] mapperLocations) - Settings settings = applicationContext.getBean(Settings.class); Resource[] mapperLocations = settings.getMapperLocations(); sqlSessionFactoryBean.setMapperLocations(mapperLocations); return sqlSessionFactoryBean; } + + @Bean + public DynamicRoutingDataSource dynamicRoutingDataSource(Settings settings) { + DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource(); + Map dataSourceDtoMap = settings.getDataSourceDtoMap(); + for(String dataSourceId : dataSourceDtoMap.keySet()) { + DataSourceDto dataSourceDto = dataSourceDtoMap.get(dataSourceId); + BasicDataSource dataSource = new BasicDataSource(); + dataSource.setDriverClassName(dataSourceDto.getDriverClassName()); + dataSource.setUrl(dataSourceDto.getUrl()); + dataSource.setUsername(dataSourceDto.getUsername()); + dataSource.setPassword(dataSourceDto.getPassword()); + if(oracle.jdbc.driver.OracleDriver.class.getCanonicalName().equals(dataSourceDto.getDriverClassName())) { + dataSource.setValidationQuery("SELECT 1 FROM DUAL"); + } + else if(org.postgresql.Driver.class.getCanonicalName().equals(dataSourceDto.getDriverClassName())) { + dataSource.setValidationQuery("SELECT 1"); + } + else if(com.mysql.jdbc.Driver.class.getCanonicalName().equals(dataSourceDto.getDriverClassName())) { + dataSource.setValidationQuery("SELECT 1"); + } + else if(org.mariadb.jdbc.Driver.class.getCanonicalName().equals(dataSourceDto.getDriverClassName())) { + dataSource.setValidationQuery("SELECT 1"); + } + else if(com.ibm.db2.jcc.DB2Driver.class.getCanonicalName().equals(dataSourceDto.getDriverClassName())) { + dataSource.setValidationQuery("SELECT 1 FROM SYSIBM.SYSDUMMY1"); + } + dataSource.setTestOnBorrow(false); + dataSource.setTestOnReturn(false); + dataSource.setTestOnCreate(true); + dataSource.setTestWhileIdle(true); + dataSource.setInitialSize(3); + dataSource.setMinIdle(3); + dataSource.setMaxIdle(30); + dataSource.setMaxTotal(30); + dynamicRoutingDataSource.addDataSource(dataSourceId, dataSource); + } + return dynamicRoutingDataSource; + } } diff --git a/src/main/java/com/bsmlab/dfx/agent/config/DynamicDataSourceConfig.java b/src/main/java/com/bsmlab/dfx/agent/config/DynamicDataSourceConfig.java deleted file mode 100644 index 86bb0aa..0000000 --- a/src/main/java/com/bsmlab/dfx/agent/config/DynamicDataSourceConfig.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.bsmlab.dfx.agent.config; - -import org.apache.ibatis.session.SqlSessionFactory; -import org.apache.ibatis.datasource.pooled.PooledDataSource; -import org.mybatis.spring.SqlSessionFactoryBean; -import org.mybatis.spring.SqlSessionTemplate; -import org.mybatis.spring.annotation.MapperScan; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.jdbc.datasource.DataSourceTransactionManager; - -import javax.sql.DataSource; -import java.util.HashMap; -import java.util.Map; - -@Configuration -@MapperScan(basePackages = "com.bsmlab.dfx.agent", sqlSessionTemplateRef = "dynamicSqlSessionTemplate") -public class DynamicDataSourceConfig { - - @Bean - public DynamicRoutingDataSource dynamicRoutingDataSource() { - DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource(); - - // 기본 데이터소스 등록 - DataSource defaultDataSource = createDataSource("jdbc:mysql://localhost:3306/default_db", "root", "password"); - dynamicRoutingDataSource.addDataSource("default", defaultDataSource); - - return dynamicRoutingDataSource; - } - - @Bean - public DataSourceTransactionManager transactionManager(DynamicRoutingDataSource dynamicRoutingDataSource) { - return new DataSourceTransactionManager(dynamicRoutingDataSource); - } - - @Bean - public SqlSessionFactory sqlSessionFactory(DynamicRoutingDataSource dynamicRoutingDataSource) throws Exception { - SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); - sessionFactory.setDataSource(dynamicRoutingDataSource); - return sessionFactory.getObject(); - } - - @Bean - public SqlSessionTemplate dynamicSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { - return new SqlSessionTemplate(sqlSessionFactory); - } - - private DataSource createDataSource(String url, String username, String password) { - // DBCP2 Connection Pool 사용 - PooledDataSource dataSource = new PooledDataSource(); - dataSource.setDriver("com.mysql.cj.jdbc.Driver"); - dataSource.setUrl(url); - dataSource.setUsername(username); - dataSource.setPassword(password); - return dataSource; - } -} diff --git a/src/main/java/com/bsmlab/dfx/agent/config/DynamicDataSourceService.java b/src/main/java/com/bsmlab/dfx/agent/config/DynamicDataSourceService.java deleted file mode 100644 index 7f967e2..0000000 --- a/src/main/java/com/bsmlab/dfx/agent/config/DynamicDataSourceService.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.bsmlab.dfx.agent.config; - -import org.apache.ibatis.datasource.pooled.PooledDataSource; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import javax.sql.DataSource; - -@Service -public class DynamicDataSourceService { - - @Autowired - private DynamicRoutingDataSource dynamicRoutingDataSource; - - public void addNewDataSource(String dataSourceName, String driver, String url, String username, String password) { - DataSource newDataSource = createDataSource(driver, url, username, password); - dynamicRoutingDataSource.addDataSource(dataSourceName, newDataSource); - System.out.println("✅ 새로운 데이터소스 추가됨: " + dataSourceName); - } - - private DataSource createDataSource(String driver, String url, String username, String password) { - PooledDataSource dataSource = new PooledDataSource(); - dataSource.setDriver(driver); - dataSource.setUrl(url); - dataSource.setUsername(username); - dataSource.setPassword(password); - return dataSource; - } -} diff --git a/src/main/java/com/bsmlab/dfx/agent/config/Settings.java b/src/main/java/com/bsmlab/dfx/agent/config/Settings.java index 22094ca..7a9f78b 100644 --- a/src/main/java/com/bsmlab/dfx/agent/config/Settings.java +++ b/src/main/java/com/bsmlab/dfx/agent/config/Settings.java @@ -20,7 +20,7 @@ import java.util.Map; @Slf4j @Component public class Settings { - private Map dataSourceDtoMap = new HashMap<>(); + private final Map dataSourceDtoMap = new HashMap<>(); private Resource[] mapperLocations; public void loadSettingFile(String settingFilePath) { try { @@ -66,4 +66,8 @@ public class Settings { public Resource[] getMapperLocations() { return this.mapperLocations; } + + public Map getDataSourceDtoMap() { + return this.dataSourceDtoMap; + } } diff --git a/src/main/java/com/bsmlab/dfx/agent/config/UserMapper.java b/src/main/java/com/bsmlab/dfx/agent/config/UserMapper.java deleted file mode 100644 index d0471d0..0000000 --- a/src/main/java/com/bsmlab/dfx/agent/config/UserMapper.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.bsmlab.dfx.agent.config; - -import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Select; -import java.util.List; -import java.util.Map; - -@Mapper -public interface UserMapper { - @Select("SELECT * FROM users") - List> findAll(); -} diff --git a/src/main/java/com/bsmlab/dfx/agent/config/UserService.java b/src/main/java/com/bsmlab/dfx/agent/config/UserService.java deleted file mode 100644 index 66275d1..0000000 --- a/src/main/java/com/bsmlab/dfx/agent/config/UserService.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.bsmlab.dfx.agent.config; - -import lombok.RequiredArgsConstructor; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@Service -@RequiredArgsConstructor -public class UserService { - private final DynamicRoutingDataSource dynamicRoutingDataSource; - private UserMapper userMapper; - - public void fetchUsersFromDefault() { - dynamicRoutingDataSource.setDataSource("default"); - System.out.println("🔹 Default DB에서 데이터 조회"); - System.out.println(userMapper.findAll()); - } - - @Transactional - public void fetchUsersFromNewDB() { - dynamicRoutingDataSource.setDataSource("newDB"); - System.out.println("🔹 New DB에서 데이터 조회"); - System.out.println(userMapper.findAll()); - } -} diff --git a/src/main/java/com/bsmlab/dfx/agent/config/datasource/DataSourcePool.java b/src/main/java/com/bsmlab/dfx/agent/config/datasource/DataSourcePool.java index 7e9b251..8177e24 100644 --- a/src/main/java/com/bsmlab/dfx/agent/config/datasource/DataSourcePool.java +++ b/src/main/java/com/bsmlab/dfx/agent/config/datasource/DataSourcePool.java @@ -6,4 +6,5 @@ import org.springframework.stereotype.Component; @Slf4j @Component public class DataSourcePool { + } diff --git a/src/main/java/com/bsmlab/dfx/agent/config/datasource/DynamicDataSourceService.java b/src/main/java/com/bsmlab/dfx/agent/config/datasource/DynamicDataSourceService.java new file mode 100644 index 0000000..9f56344 --- /dev/null +++ b/src/main/java/com/bsmlab/dfx/agent/config/datasource/DynamicDataSourceService.java @@ -0,0 +1,32 @@ +package com.bsmlab.dfx.agent.config.datasource; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.datasource.pooled.PooledDataSource; +import org.apache.ibatis.session.SqlSessionFactory; +import org.mybatis.spring.SqlSessionFactoryBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.stereotype.Service; +import javax.sql.DataSource; +import java.util.HashMap; +import java.util.Map; + +@RequiredArgsConstructor +@Slf4j +@Service +public class DynamicDataSourceService { + private final DynamicRoutingDataSource dynamicRoutingDataSource; + private final RefreshableSqlSessionFactoryBean refreshableSqlSessionFactoryBean; + private final Map sqlSessionFactoryMap = new HashMap<>(); + private final Map transactionManagerMap = new HashMap<>(); + + public SqlSessionFactory getSqlSessionFactory(String dataSourceId) { + if(!this.sqlSessionFactoryMap.containsKey(dataSourceId)) { + DataSource dataSource = this.dynamicRoutingDataSource.getDataSourceMap().get(dataSourceId); + refreshableSqlSessionFactoryBean.setDataSource(dataSource); + this.sqlSessionFactoryMap.put(dataSourceId, refreshableSqlSessionFactoryBean.getObject()); + } + return this.sqlSessionFactoryMap.get(dataSourceId); + } +} diff --git a/src/main/java/com/bsmlab/dfx/agent/config/DynamicRoutingDataSource.java b/src/main/java/com/bsmlab/dfx/agent/config/datasource/DynamicRoutingDataSource.java similarity index 50% rename from src/main/java/com/bsmlab/dfx/agent/config/DynamicRoutingDataSource.java rename to src/main/java/com/bsmlab/dfx/agent/config/datasource/DynamicRoutingDataSource.java index 3d96334..692d357 100644 --- a/src/main/java/com/bsmlab/dfx/agent/config/DynamicRoutingDataSource.java +++ b/src/main/java/com/bsmlab/dfx/agent/config/datasource/DynamicRoutingDataSource.java @@ -1,14 +1,15 @@ -package com.bsmlab.dfx.agent.config; +package com.bsmlab.dfx.agent.config.datasource; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; +import org.springframework.stereotype.Component; + import javax.sql.DataSource; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public class DynamicRoutingDataSource extends AbstractRoutingDataSource { - private static final ThreadLocal contextHolder = new ThreadLocal<>(); - private final Map dataSourceMap = new ConcurrentHashMap<>(); + private final Map dataSourceMap = new ConcurrentHashMap<>(); @Override protected Object determineCurrentLookupKey() { @@ -23,13 +24,17 @@ public class DynamicRoutingDataSource extends AbstractRoutingDataSource { contextHolder.remove(); } - public void addDataSource(String name, DataSource dataSource) { - dataSourceMap.put(name, dataSource); - super.setTargetDataSources(dataSourceMap); - super.afterPropertiesSet(); + public Map getDataSourceMap() { + return this.dataSourceMap; } - public Map getDataSourceMap() { - return dataSourceMap; + public void addDataSource(String dataSourceId, DataSource dataSource) { + this.dataSourceMap.put(dataSourceId, dataSource); + Map castingMap = new ConcurrentHashMap<>(); + for(Object key : this.dataSourceMap.keySet()) { + castingMap.put(key, this.dataSourceMap.get(key)); + } + super.setTargetDataSources(castingMap); + super.afterPropertiesSet(); } } diff --git a/src/main/java/com/bsmlab/dfx/agent/config/datasource/RefreshableSqlSessionFactoryBean.java b/src/main/java/com/bsmlab/dfx/agent/config/datasource/RefreshableSqlSessionFactoryBean.java index c6d6c86..b2eb3f5 100644 --- a/src/main/java/com/bsmlab/dfx/agent/config/datasource/RefreshableSqlSessionFactoryBean.java +++ b/src/main/java/com/bsmlab/dfx/agent/config/datasource/RefreshableSqlSessionFactoryBean.java @@ -4,7 +4,6 @@ import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.springframework.beans.factory.DisposableBean; -import org.springframework.context.annotation.DependsOn; import org.springframework.core.io.Resource; import org.springframework.stereotype.Component; @@ -18,7 +17,6 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; @Slf4j @Component -@DependsOn(value = {"settings"}) public class RefreshableSqlSessionFactoryBean extends SqlSessionFactoryBean implements DisposableBean { private SqlSessionFactory proxy; private int interval = 500; diff --git a/src/main/java/com/bsmlab/dfx/agent/config/datasource/SqlExecuteService.java b/src/main/java/com/bsmlab/dfx/agent/config/datasource/SqlExecuteService.java new file mode 100644 index 0000000..b898645 --- /dev/null +++ b/src/main/java/com/bsmlab/dfx/agent/config/datasource/SqlExecuteService.java @@ -0,0 +1,79 @@ +package com.bsmlab.dfx.agent.config.datasource; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.session.SqlSession; +import org.apache.ibatis.session.SqlSessionFactory; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +@RequiredArgsConstructor +@Service +@Slf4j +public class SqlExecuteService { + private final DynamicRoutingDataSource dynamicRoutingDataSource; + private final DynamicDataSourceService dynamicDataSourceService; + + public List> select(String dataSourceId, String sqlId, Map parameter) { + dynamicRoutingDataSource.setDataSource(dataSourceId); + try { + SqlSessionFactory sqlSessionFactory = dynamicDataSourceService.getSqlSessionFactory(dataSourceId); + SqlSession sqlSession = sqlSessionFactory.openSession(); + return sqlSession.selectList(sqlId, parameter); + } + finally { + dynamicRoutingDataSource.clearDataSource(); + } + } + + public Map insert(String dataSourceId, String sqlId, Map parameter) { + dynamicRoutingDataSource.setDataSource(dataSourceId); + try { + SqlSessionFactory sqlSessionFactory = dynamicDataSourceService.getSqlSessionFactory(dataSourceId); + SqlSession sqlSession = sqlSessionFactory.openSession(); + sqlSession.insert(sqlId, parameter); + return parameter; + } + finally { + dynamicRoutingDataSource.clearDataSource(); + } + } + + public int update(String dataSourceId, String sqlId, Map parameter) { + dynamicRoutingDataSource.setDataSource(dataSourceId); + try { + SqlSessionFactory sqlSessionFactory = dynamicDataSourceService.getSqlSessionFactory(dataSourceId); + SqlSession sqlSession = sqlSessionFactory.openSession(); + return sqlSession.update(sqlId, parameter); + } + finally { + dynamicRoutingDataSource.clearDataSource(); + } + } + + public int delete(String dataSourceId, String sqlId, Map parameter) { + dynamicRoutingDataSource.setDataSource(dataSourceId); + try { + SqlSessionFactory sqlSessionFactory = dynamicDataSourceService.getSqlSessionFactory(dataSourceId); + SqlSession sqlSession = sqlSessionFactory.openSession(); + return sqlSession.delete(sqlId, parameter); + } + finally { + dynamicRoutingDataSource.clearDataSource(); + } + } + + public void execute(String dataSourceId, String sqlId, Map parameter) { + dynamicRoutingDataSource.setDataSource(dataSourceId); + try { + SqlSessionFactory sqlSessionFactory = dynamicDataSourceService.getSqlSessionFactory(dataSourceId); + SqlSession sqlSession = sqlSessionFactory.openSession(); + sqlSession.selectOne(sqlId, parameter); + } + finally { + dynamicRoutingDataSource.clearDataSource(); + } + } +}