동적 다중 데이터소스, sqlSession 작업 완료

내부 데이터소스 sqlSession 작업 완료
main
semin.baek 11 months ago
parent 92ff4df5fe
commit 3ded2cb238

@ -1,20 +1,34 @@
package com.bsmlab.dfx.agent.config; package com.bsmlab.dfx.agent.config;
import com.bsmlab.dfx.agent.config.datasource.DataSourceDto; import com.bsmlab.dfx.agent.config.datasource.DataSourceDto;
import com.bsmlab.dfx.agent.config.datasource.DynamicDataSourceService;
import com.bsmlab.dfx.agent.config.datasource.DynamicRoutingDataSource; import com.bsmlab.dfx.agent.config.datasource.DynamicRoutingDataSource;
import com.bsmlab.dfx.agent.config.datasource.RefreshableSqlSessionFactoryBean;
import io.micrometer.common.util.StringUtils; import io.micrometer.common.util.StringUtils;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.dbcp2.BasicDataSource; import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan; import org.mybatis.spring.annotation.MapperScan;
import org.mybatis.spring.annotation.MapperScans; import org.mybatis.spring.annotation.MapperScans;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
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.core.io.Resource; import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
@Slf4j @Slf4j
@ -24,18 +38,23 @@ import java.util.Map;
@MapperScan("com.bsmlab.dfx.agent") @MapperScan("com.bsmlab.dfx.agent")
}) })
public class DfxAgentConfiguration { public class DfxAgentConfiguration {
private static final String DB_SOURCE_FILE_PATH = "./storages/dfxagent.mv.db";
// gradle bootRun 실행 설정 // gradle bootRun 실행 설정
// bootRun --args="--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" // bootRun --args="--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"
// command line java 실행 설정 // command line java 실행 설정
// 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 // 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 //example: embedded.db.file.directory=D:\projects\bsm-lab\dfx\dfxagent\src\docs\settings-examples
@Value("${embedded.db.file.directory}") @Value("${embedded.db.file.directory}")
private String embeddedDbFileDirectory; private String embeddedDbFileDirectory;
// setting.file=D:\projects\bsm-lab\dfx\dfxagent\src\docs\settings-examples\dfxagent.json //example: setting.file=D:\projects\bsm-lab\dfx\dfxagent\src\docs\settings-examples\dfxagent.json
@Value("${setting.file}") @Value("${setting.file}")
private String settingFile; private String settingFile;
private Map<String, SqlSessionFactory> temporarySqlSessionFactoryMap = new HashMap<>();
Map<String, DataSourceTransactionManager> temporaryTransactionManagerMap = new HashMap<>();
@Bean("settings") @Bean("settings")
public Settings loadSettings() { public Settings loadSettings() {
if(StringUtils.isBlank(this.embeddedDbFileDirectory)) { if(StringUtils.isBlank(this.embeddedDbFileDirectory)) {
@ -53,17 +72,110 @@ public class DfxAgentConfiguration {
return settings; return settings;
} }
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(Settings settings) {
RefreshableSqlSessionFactoryBean sqlSessionFactoryBean = new RefreshableSqlSessionFactoryBean();
Resource[] mapperLocations = settings.getMapperLocations();
sqlSessionFactoryBean.setMapperLocations(mapperLocations);
return sqlSessionFactoryBean;
}
@Bean(name = "dynamicRoutingDataSource") @Bean(name = "dynamicRoutingDataSource")
public DynamicRoutingDataSource dynamicRoutingDataSource(Settings settings) { public DynamicRoutingDataSource dynamicRoutingDataSource(Settings settings) {
DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource(); DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource();
Map<String, SqlSessionFactory> sqlSessionFactoryMap = new HashMap<>();
Map<String, DataSourceTransactionManager> transactionManagerMap = new HashMap<>();
Map<String, DataSourceDto> 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);
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
try {
sqlSessionFactoryMap.put(dataSourceId, sqlSessionFactoryBean.getObject());
} catch (Exception e) {
log.error("DynamicRoutingDataSource 생성 중 오류: {}", e.getMessage(), e);
throw new RuntimeException(e);
}
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
transactionManagerMap.put(dataSourceId, transactionManager);
dynamicRoutingDataSource.addDataSource(dataSourceId, dataSource);
}
this.temporarySqlSessionFactoryMap = sqlSessionFactoryMap;
this.temporaryTransactionManagerMap = transactionManagerMap;
return dynamicRoutingDataSource; return dynamicRoutingDataSource;
} }
@Bean(name = "dynamicDataSourceService")
public DynamicDataSourceService dynamicDataSourceService() {
DynamicDataSourceService dynamicDataSourceService = new DynamicDataSourceService();
dynamicDataSourceService.setSqlSessionFactoryMap(this.temporarySqlSessionFactoryMap);
dynamicDataSourceService.setTransactionManagerMap(this.temporaryTransactionManagerMap);
return dynamicDataSourceService;
}
public void copyEmbeddedDbFileIfNotExists() {
String embeddedDbFileDirectory = System.getProperty("embedded.db.file.directory");
File targetDirectory = new File(embeddedDbFileDirectory);
if(!targetDirectory.exists()) {
targetDirectory = new File(System.getProperty("user.home"));
}
File targetFile = new File(targetDirectory.getAbsoluteFile() + "/dfxagent.mv.db");
log.debug("embeddedDbFilePath: {}", embeddedDbFileDirectory);
if(!targetFile.exists()) {
try {
InputStream inputStream = new ClassPathResource(DB_SOURCE_FILE_PATH).getInputStream();
log.debug("embedded db file copy from {} to {}", DB_SOURCE_FILE_PATH, embeddedDbFileDirectory);
Files.copy(inputStream, Paths.get(targetFile.getAbsolutePath()), StandardCopyOption.REPLACE_EXISTING);
}
catch (IOException e) {
log.error("cannot make Embedded DB files. exit application.");
System.exit(0);
}
}
}
@Bean(name = "innerDataSource")
@ConfigurationProperties("spring.datasource") // application.yml 에서 설정된 값 자동 적용
public DataSource innerDataSource(DataSourceProperties dataSourceProperties) {
return dataSourceProperties.initializeDataSourceBuilder().build();
}
@Bean(name = "innerSqlSessionFactory")
public SqlSessionFactory innerSqlSessionFactory(@Qualifier("innerDataSource") DataSource dataSource, @Qualifier("settings") Settings settings) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
sqlSessionFactoryBean.setMapperLocations(settings.getMapperLocations());
return sqlSessionFactoryBean.getObject();
}
@Bean(name = "innerSqlSessionTemplate")
public SqlSessionTemplate innerSqlSessionTemplate(@Qualifier("innerSqlSessionFactory") SqlSessionFactory innerSqlSessionFactory) {
return new SqlSessionTemplate(innerSqlSessionFactory);
}
@Bean(name = "innerTransactionManager")
public DataSourceTransactionManager innerTransactionManager(@Qualifier("innerDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
} }

@ -1,6 +1,8 @@
package com.bsmlab.dfx.agent.config; package com.bsmlab.dfx.agent.config;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -12,10 +14,17 @@ import java.nio.file.Paths;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
@Slf4j @Slf4j
@Component @Configuration
public class EmbeddedDbInitializer { public class EmbeddedDbConfiguration {
private static final String DB_SOURCE_FILE_PATH = "./storages/dfxagent.mv.db"; private static final String DB_SOURCE_FILE_PATH = "./storages/dfxagent.mv.db";
//example: embedded.db.file.directory=D:\projects\bsm-lab\dfx\dfxagent\src\docs\settings-examples
@Value("${embedded.db.file.directory}")
private String embeddedDbFileDirectory;
//example: setting.file=D:\projects\bsm-lab\dfx\dfxagent\src\docs\settings-examples\dfxagent.json
@Value("${setting.file}")
private String settingFile;
public void copyEmbeddedDbFileIfNotExists() { public void copyEmbeddedDbFileIfNotExists() {
log.debug("run"); log.debug("run");

@ -58,6 +58,7 @@ public class Settings {
int i = 0; int i = 0;
for(String location : locationStringList) { for(String location : locationStringList) {
resources[i] = new InputStreamResource(new FileInputStream(new File(location))); resources[i] = new InputStreamResource(new FileInputStream(new File(location)));
i++;
} }
} }
return resources; return resources;

@ -10,7 +10,7 @@ import org.springframework.stereotype.Component;
@RequiredArgsConstructor @RequiredArgsConstructor
@Component @Component
public class StartupRunner implements ApplicationRunner { public class StartupRunner implements ApplicationRunner {
private final EmbeddedDbInitializer embeddedDbInitializer; private final EmbeddedDbConfiguration embeddedDbConfiguration;
@Override @Override
public void run(ApplicationArguments args) throws Exception { public void run(ApplicationArguments args) throws Exception {

@ -1,10 +0,0 @@
package com.bsmlab.dfx.agent.config.datasource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class DataSourcePool {
}

@ -1,80 +1,32 @@
package com.bsmlab.dfx.agent.config.datasource; package com.bsmlab.dfx.agent.config.datasource;
import com.bsmlab.dfx.agent.config.Settings;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.ibatis.datasource.pooled.PooledDataSource;
import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.DependsOn;
import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionManager;
import javax.sql.DataSource;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@RequiredArgsConstructor
@Slf4j @Slf4j
@Service @Component
@DependsOn({"settings", "dynamicRoutingDataSource"})
public class DynamicDataSourceService { public class DynamicDataSourceService {
private final Settings settings; private Map<String, SqlSessionFactory> sqlSessionFactoryMap = new HashMap<>();
private final DynamicRoutingDataSource dynamicRoutingDataSource; private Map<String, DataSourceTransactionManager> transactionManagerMap = new HashMap<>();
private final RefreshableSqlSessionFactoryBean refreshableSqlSessionFactoryBean;
private final Map<String, SqlSessionFactory> sqlSessionFactoryMap = new HashMap<>();
private final Map<String, DataSourceTransactionManager> transactionManagerMap = new HashMap<>();
public SqlSessionFactory getSqlSessionFactory(String dataSourceId) { public SqlSessionFactory getSqlSessionFactory(String dataSourceId) {
return this.sqlSessionFactoryMap.get(dataSourceId); return this.sqlSessionFactoryMap.get(dataSourceId);
} }
public TransactionManager getTransactionManager(String dataSourceId) { public DataSourceTransactionManager getTransactionManager(String dataSourceId) {
return this.transactionManagerMap.get(dataSourceId); return this.transactionManagerMap.get(dataSourceId);
} }
@PostConstruct public void setSqlSessionFactoryMap(Map<String, SqlSessionFactory> sqlSessionFactoryMap) {
public void init() { this.sqlSessionFactoryMap = sqlSessionFactoryMap;
Map<String, DataSourceDto> dataSourceDtoMap = settings.getDataSourceDtoMap(); }
for(String dataSourceId : dataSourceDtoMap.keySet()) {
DataSourceDto dataSourceDto = dataSourceDtoMap.get(dataSourceId); public void setTransactionManagerMap(Map<String, DataSourceTransactionManager> transactionManagerMap) {
BasicDataSource dataSource = new BasicDataSource(); this.transactionManagerMap = transactionManagerMap;
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);
refreshableSqlSessionFactoryBean.setDataSource(dataSource);
sqlSessionFactoryMap.put(dataSourceId, refreshableSqlSessionFactoryBean.getObject());
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
transactionManagerMap.put(dataSourceId, transactionManager);
}
} }
} }

@ -1,7 +1,6 @@
package com.bsmlab.dfx.agent.config.datasource; package com.bsmlab.dfx.agent.config.datasource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.stereotype.Component;
import javax.sql.DataSource; import javax.sql.DataSource;
import java.util.Map; import java.util.Map;

@ -5,7 +5,6 @@ import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.DisposableBean;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationHandler;
@ -16,7 +15,6 @@ import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
@Slf4j @Slf4j
@Component
public class RefreshableSqlSessionFactoryBean extends SqlSessionFactoryBean implements DisposableBean { public class RefreshableSqlSessionFactoryBean extends SqlSessionFactoryBean implements DisposableBean {
private SqlSessionFactory proxy; private SqlSessionFactory proxy;
private int interval = 500; private int interval = 500;
@ -40,12 +38,16 @@ public class RefreshableSqlSessionFactoryBean extends SqlSessionFactoryBean impl
public void refresh() throws Exception { public void refresh() throws Exception {
if(log.isInfoEnabled()) { if(log.isInfoEnabled()) {
log.info("> Refresh SqlMapper"); log.info("> Refreshing SQL Mapper Configuration...");
log.info("======================================================================================");
} }
w.lock(); w.lock();
try { try {
super.afterPropertiesSet(); super.afterPropertiesSet();
// refresh()가 호출될 때 기존 proxy를 재생성하여 기존 세션과 충돌을 방지한다.
proxy = (SqlSessionFactory) Proxy.newProxyInstance(
SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSessionFactory.class}
, (proxy, method, args) -> method.invoke(getParentObject(), args)
);
} finally { } finally {
w.unlock(); w.unlock();
} }

@ -18,9 +18,7 @@ public class SqlExecuteService {
public List<Map<String, Object>> select(String dataSourceId, String sqlId, Map<String, Object> parameter) { public List<Map<String, Object>> select(String dataSourceId, String sqlId, Map<String, Object> parameter) {
dynamicRoutingDataSource.setDataSource(dataSourceId); dynamicRoutingDataSource.setDataSource(dataSourceId);
try { try(SqlSession sqlSession = dynamicDataSourceService.getSqlSessionFactory(dataSourceId).openSession()) {
SqlSessionFactory sqlSessionFactory = dynamicDataSourceService.getSqlSessionFactory(dataSourceId);
SqlSession sqlSession = sqlSessionFactory.openSession();
return sqlSession.selectList(sqlId, parameter); return sqlSession.selectList(sqlId, parameter);
} }
finally { finally {
@ -30,9 +28,7 @@ public class SqlExecuteService {
public Map<String, Object> insert(String dataSourceId, String sqlId, Map<String, Object> parameter) { public Map<String, Object> insert(String dataSourceId, String sqlId, Map<String, Object> parameter) {
dynamicRoutingDataSource.setDataSource(dataSourceId); dynamicRoutingDataSource.setDataSource(dataSourceId);
try { try(SqlSession sqlSession = dynamicDataSourceService.getSqlSessionFactory(dataSourceId).openSession()) {
SqlSessionFactory sqlSessionFactory = dynamicDataSourceService.getSqlSessionFactory(dataSourceId);
SqlSession sqlSession = sqlSessionFactory.openSession();
sqlSession.insert(sqlId, parameter); sqlSession.insert(sqlId, parameter);
return parameter; return parameter;
} }
@ -43,9 +39,7 @@ public class SqlExecuteService {
public int update(String dataSourceId, String sqlId, Map<String, Object> parameter) { public int update(String dataSourceId, String sqlId, Map<String, Object> parameter) {
dynamicRoutingDataSource.setDataSource(dataSourceId); dynamicRoutingDataSource.setDataSource(dataSourceId);
try { try(SqlSession sqlSession = dynamicDataSourceService.getSqlSessionFactory(dataSourceId).openSession()) {
SqlSessionFactory sqlSessionFactory = dynamicDataSourceService.getSqlSessionFactory(dataSourceId);
SqlSession sqlSession = sqlSessionFactory.openSession();
return sqlSession.update(sqlId, parameter); return sqlSession.update(sqlId, parameter);
} }
finally { finally {
@ -55,9 +49,7 @@ public class SqlExecuteService {
public int delete(String dataSourceId, String sqlId, Map<String, Object> parameter) { public int delete(String dataSourceId, String sqlId, Map<String, Object> parameter) {
dynamicRoutingDataSource.setDataSource(dataSourceId); dynamicRoutingDataSource.setDataSource(dataSourceId);
try { try(SqlSession sqlSession = dynamicDataSourceService.getSqlSessionFactory(dataSourceId).openSession()) {
SqlSessionFactory sqlSessionFactory = dynamicDataSourceService.getSqlSessionFactory(dataSourceId);
SqlSession sqlSession = sqlSessionFactory.openSession();
return sqlSession.delete(sqlId, parameter); return sqlSession.delete(sqlId, parameter);
} }
finally { finally {

Loading…
Cancel
Save