package com.test;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.core.io.Resource;
import java.io.IOException;
import java.lang.reflect.Proxy;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class RefreshableSqlSessionFactoryBean extends SqlSessionFactoryBean implements DisposableBean {
private static final Logger logger = LoggerFactory.getLogger(RefreshableSqlSessionFactoryBean.class);
private SqlSessionFactory proxy;
private int interval = 500;
private Timer timer;
private TimerTask task;
private Resource[] refreshableMapperLocations;
private boolean running = false;
private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
private final Lock r = rwl.readLock();
private final Lock w = rwl.writeLock();
public RefreshableSqlSessionFactoryBean() {
}
@Override
public void afterPropertiesSet() throws Exception {
super.afterPropertiesSet();
setRefreshable();
}
public void setRefreshableMapperLocations(Resource[] refreshableMapperLocations) {
this.refreshableMapperLocations = refreshableMapperLocations;
}
public void setInterval(int ms) {
this.interval = ms;
}
public void setCheckInterval(int ms) {
interval = ms;
if (timer != null) {
resetInterval();
}
}
private void refresh() throws Exception {
if (logger.isInfoEnabled()) {
logger.info("> Refresh SqlMapper...");
}
w.lock();
try {
super.afterPropertiesSet();
logger.info("> Done.");
logger.info("======================================================================================");
} finally {
w.unlock();
}
}
@SuppressWarnings("java:S3776")
private void setRefreshable() {
proxy = (SqlSessionFactory) Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(),
new Class[]{SqlSessionFactory.class},
(aProxy, method, args) -> method.invoke(getParentObject(),
args));
task = new TimerTask() {
private final Map<Resource, Long> map = new HashMap<>();
@Override
public void run() {
if (isModified()) {
try {
refresh();
} catch (Exception e) {
logger.error("caught exception", e);
}
}
}
private boolean isModified() {
if (refreshableMapperLocations != null) {
List<String> modifiedResources = new ArrayList<>();
for (Resource mappingLocation : refreshableMapperLocations) {
modifiedResources.addAll(findModifiedResource(mappingLocation));
}
if (!modifiedResources.isEmpty()) {
if(logger.isInfoEnabled()) {
logger.info("======================================================================================");
logger.info("> Update File name : {}", modifiedResources);
}
return true;
}
}
return false;
}
private List<String> findModifiedResource(Resource resource) {
List<String> modifiedResources = new ArrayList<>();
try {
long modified = resource.lastModified();
if (map.containsKey(resource)) {
long lastModified = map.get(resource);
if (lastModified != modified) {
map.put(resource, modified);
//modifiedResources.add(resource.getDescription()); // 전체경로
modifiedResources.add(resource.getFilename()); // 파일명
}
} else {
map.put(resource, modified);
}
} catch (IOException e) {
logger.error("caught exception", e);
}
return modifiedResources;
}
};
timer = new Timer(true);
resetInterval();
}
private SqlSessionFactory getParentObject() throws Exception {
r.lock();
try {
return super.getObject();
} finally {
r.unlock();
}
}
private void resetInterval() {
if (running) {
timer.cancel();
running = false;
}
if (interval > 0) {
timer.schedule(task, 0, interval);
running = true;
}
}
@Override
public SqlSessionFactory getObject() {
try {
getParentObject(); // build factory if necessary
} catch (Exception e) {
e.printStackTrace();
}
return this.proxy;
}
@Override
public Class<? extends SqlSessionFactory> getObjectType() {
return (this.proxy != null ? this.proxy.getClass() : SqlSessionFactory.class);
}
@Override
public boolean isSingleton() {
return true;
}
@Override
public void destroy() {
timer.cancel();
}
}
package com.test;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.Resource;
import javax.sql.DataSource;
import java.io.IOException;
public class NodeConfigurationHelper {
private NodeConfigurationHelper() {
}
static SqlSessionFactory createRefreshableSqlSessionFactory(ApplicationContext applicationContext,
DataSource dataSource) throws IOException {
RefreshableSqlSessionFactoryBean sqlSessionFactoryBean = new RefreshableSqlSessionFactoryBean();
setSqlSessionFactoryBeanCommonSettings(sqlSessionFactoryBean, applicationContext, dataSource);
// for refresh query
sqlSessionFactoryBean.setInterval(1000);
Resource[] mapper1 = applicationContext.getResources("classpath:/mapper/tibero/**/*.xml");
Resource[] mapper2 = applicationContext.getResources("classpath:/com/daou/sabangnet/dao/**/*.xml");
Resource[] mappers = ArrayUtils.addAll(mapper1, mapper2);
sqlSessionFactoryBean.setRefreshableMapperLocations(mappers);
return sqlSessionFactoryBean.getObject();
}
static void setSqlSessionFactoryBeanCommonSettings(SqlSessionFactoryBean sqlSessionFactoryBean,
ApplicationContext applicationContext,
DataSource dataSource) throws IOException {
sqlSessionFactoryBean.setDataSource(dataSource);
sqlSessionFactoryBean.setConfigLocation(applicationContext.getResource("classpath:/mapper/mybatis-config.xml"));
sqlSessionFactoryBean.setMapperLocations(applicationContext.getResources("classpath:/mapper/tibero/*.xml"));
}
}
package com.test;
import com.zaxxer.hikari.HikariConfig;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;
import java.io.IOException;
@Slf4j
@Configuration
@PropertySource("classpath:/application.properties")
public class Node01DatabaseConfiguration {
@Autowired
private ApplicationContext applicationContext;
@Value("${spring.datasource.node01.connection-pool-size}")
private Integer maxConnectionPool;
@Bean(name="node01Configuration")
@ConfigurationProperties(prefix="spring.datasource.node01")
public HikariConfig node01Config() {
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setMaximumPoolSize(maxConnectionPool);
return hikariConfig;
}
@Bean(name="node01DataSource")
public DataSource node01DataSource() {
HikariConfig firstConfig = node01Config();
log.info("Max. Hikari Connections for Node01 DB: {}", firstConfig.getMaximumPoolSize());
DataSource dataSource = new HikariDataSource(node01Config());
log.info("datasource : {}", dataSource);
return dataSource;
}
@Bean(name="node01SessionFactory")
@Profile("local")
public SqlSessionFactory refreshableSqlSessionFactory(@Qualifier("node01DataSource") DataSource dataSource)
throws IOException {
return NodeConfigurationHelper.createRefreshableSqlSessionFactory(applicationContext, dataSource);
}
@Bean(name="node01SessionFactory")
@Profile("!local")
public SqlSessionFactory sqlSessionFactory(@Qualifier("node01DataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
NodeConfigurationHelper.setSqlSessionFactoryBeanCommonSettings(sqlSessionFactoryBean, applicationContext, dataSource);
return sqlSessionFactoryBean.getObject();
}
@Bean(name="01TrManager")
public DataSourceTransactionManager returnTransactionManager(@Qualifier("node01DataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean(name="node01SessionTemplate")
public SqlSessionTemplate sqlSessionTemplate(@Qualifier("node01SessionFactory") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
}