diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/service/datasource/CreateDataSource.java b/ruoyi-common/src/main/java/com/ruoyi/common/service/datasource/CreateDataSource.java new file mode 100644 index 0000000..b6ac794 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/service/datasource/CreateDataSource.java @@ -0,0 +1,9 @@ +package com.ruoyi.common.service.datasource; + +import javax.sql.DataSource; + +import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; + +public interface CreateDataSource { + DataSource createDataSource(String name, DataSourceProperties dataSourceProperties); +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/AtomikosConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/AtomikosConfig.java index 46d87ef..41d0056 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/AtomikosConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/AtomikosConfig.java @@ -1,5 +1,8 @@ package com.ruoyi.framework.config; +import java.util.ArrayList; +import java.util.List; + import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.DependsOn; @@ -8,7 +11,9 @@ import org.springframework.transaction.jta.JtaTransactionManager; import com.atomikos.icatch.jta.UserTransactionImp; import com.atomikos.icatch.jta.UserTransactionManager; +import com.atomikos.jdbc.AtomikosDataSourceBean; +import jakarta.annotation.PreDestroy; import jakarta.transaction.TransactionManager; import jakarta.transaction.UserTransaction; @@ -18,11 +23,9 @@ import jakarta.transaction.UserTransaction; * @author ruoyi */ @Configuration -public class AtomikosConfig -{ +public class AtomikosConfig { @Bean(name = "userTransaction") - public UserTransaction userTransaction() throws Throwable - { + public UserTransaction userTransaction() throws Throwable { UserTransaction userTransaction = new UserTransactionImp(); // 设置事务超时时间为10000毫秒 userTransaction.setTransactionTimeout(10000); @@ -30,8 +33,7 @@ public class AtomikosConfig } @Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close") - public TransactionManager atomikosTransactionManager() throws Throwable - { + public TransactionManager atomikosTransactionManager() throws Throwable { UserTransactionManager userTransactionManager = new UserTransactionManager(); // 设置是否强制关闭事务管理器为false userTransactionManager.setForceShutdown(false); @@ -40,10 +42,22 @@ public class AtomikosConfig @Bean(name = "transactionManager") @DependsOn({ "userTransaction", "atomikosTransactionManager" }) - public PlatformTransactionManager transactionManager() throws Throwable - { + public PlatformTransactionManager transactionManager() throws Throwable { UserTransaction userTransaction = userTransaction(); TransactionManager atomikosTransactionManager = atomikosTransactionManager(); return new JtaTransactionManager(userTransaction, atomikosTransactionManager); } + + private List atomikosDataSourceBeans = new ArrayList<>(); + + public List getAtomikosDataSourceBeans() { + return atomikosDataSourceBeans; + } + + @PreDestroy + public void destroy() { + for (AtomikosDataSourceBean aDataSourceBean : this.atomikosDataSourceBeans) { + aDataSourceBean.close(); + } + } } \ No newline at end of file diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/AtomikosDataSourceCreate.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/AtomikosDataSourceCreate.java new file mode 100644 index 0000000..cdcd858 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/AtomikosDataSourceCreate.java @@ -0,0 +1,48 @@ +package com.ruoyi.framework.config; + +import java.util.Properties; + +import javax.sql.DataSource; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; +import org.springframework.context.annotation.DependsOn; +import org.springframework.stereotype.Component; + +import com.alibaba.druid.pool.xa.DruidXADataSource; +import com.atomikos.jdbc.AtomikosDataSourceBean; +import com.ruoyi.common.service.datasource.CreateDataSource; + +@Component +@DependsOn({ "transactionManager" }) +public class AtomikosDataSourceCreate implements CreateDataSource { + + private static final Logger logger = LoggerFactory.getLogger(AtomikosDataSourceCreate.class); + @Autowired + private DynamicDataSourceProperties properties; + + @Autowired + private DruidConfig druidConfig; + + @Autowired + private AtomikosConfig atomikosConfig; + + public DataSource createDataSource(String name, DataSourceProperties dataSourceProperties) { + Properties prop = properties.build(dataSourceProperties); + DruidXADataSource dataSource = new DruidXADataSource(); + druidConfig.getDruidDataSources().add(dataSource); + dataSource.setConnectProperties(prop); + properties.setProperties(dataSource, prop); + AtomikosDataSourceBean ds = new AtomikosDataSourceBean(); + atomikosConfig.getAtomikosDataSourceBeans().add(ds); + ds.setXaDataSourceClassName("com.alibaba.druid.pool.xa.DruidXADataSource"); + ds.setUniqueResourceName(name); + ds.setXaProperties(prop); + ds.setXaDataSource(dataSource); + properties.validateDataSource(ds); + logger.info("数据源:{} 链接成功", name); + return ds; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java index 41a79fd..c498928 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java @@ -1,10 +1,8 @@ package com.ruoyi.framework.config; import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -import javax.sql.DataSource; +import java.util.ArrayList; +import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -13,12 +11,12 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; +import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.spring.boot3.autoconfigure.properties.DruidStatProperties; import com.alibaba.druid.util.Utils; -import com.ruoyi.framework.datasource.DynamicDataSource; +import jakarta.annotation.PreDestroy; import jakarta.servlet.Filter; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; @@ -36,18 +34,9 @@ public class DruidConfig { @Autowired DynamicDataSourceProperties dataSourceProperties; - Logger logger = LoggerFactory.getLogger(DruidConfig.class); + private List druidDataSources = new ArrayList<>(); - @Bean(name = "dynamicDataSource") - @Primary - public DynamicDataSource dataSource() { - Map objectMap = new HashMap<>(); - Map targetDataSources = dataSourceProperties.getTargetDataSources(); - for (Map.Entry entry : targetDataSources.entrySet()) { - objectMap.put(entry.getKey(), entry.getValue()); - } - return new DynamicDataSource(targetDataSources.get(dataSourceProperties.getPrimary()), objectMap); - } + Logger logger = LoggerFactory.getLogger(DruidConfig.class); /** * 去除监控页面底部的广告 @@ -91,4 +80,15 @@ public class DruidConfig { registrationBean.addUrlPatterns(commonJsPattern); return registrationBean; } + + public List getDruidDataSources() { + return druidDataSources; + } + + @PreDestroy + public void destroy() { + for (DruidDataSource druidDataSource : druidDataSources) { + druidDataSource.close(); + } + } } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/DynamicDataSourceProperties.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/DynamicDataSourceProperties.java index fe6bbeb..9728213 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/DynamicDataSourceProperties.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/DynamicDataSourceProperties.java @@ -4,56 +4,27 @@ import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; -import java.util.HashMap; import java.util.Map; import java.util.Properties; import javax.sql.DataSource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.InitializingBean; import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.DependsOn; import com.alibaba.druid.pool.DruidDataSource; -import com.alibaba.druid.pool.xa.DruidXADataSource; -import com.atomikos.jdbc.AtomikosDataSourceBean; import com.ruoyi.common.utils.spring.SpringUtils; import com.ruoyi.framework.config.properties.DruidProperties; @Configuration -@DependsOn({ "transactionManager" }) @ConfigurationProperties(prefix = "spring.datasource.dynamic") -public class DynamicDataSourceProperties implements InitializingBean { - private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceProperties.class); +public class DynamicDataSourceProperties { + private Map datasource; private String primary; - private Map targetDataSources = new HashMap<>(); - @Override - public void afterPropertiesSet() throws Exception { - datasource.forEach((name, props) -> targetDataSources.put(name, createDataSource(name, props))); - } - - protected DataSource createDataSource(String name, DataSourceProperties dataSourceProperties) { - Properties prop = build(dataSourceProperties); - DruidXADataSource dataSource = new DruidXADataSource(); - dataSource.setConnectProperties(prop); - setProperties(dataSource, prop); - validateDataSource(dataSource); - logger.info("数据源:{} 链接成功", name); - AtomikosDataSourceBean ds = new AtomikosDataSourceBean(); - ds.setXaDataSourceClassName("com.alibaba.druid.pool.xa.DruidXADataSource"); - ds.setUniqueResourceName(name); - ds.setXaProperties(prop); - ds.setXaDataSource(dataSource); - return ds; - } - - private void validateDataSource(DataSource dataSource) { + public void validateDataSource(DataSource dataSource) { try (Connection conn = dataSource.getConnection()) { String validationQuery = "SELECT 1"; try (Statement stmt = conn.createStatement(); @@ -143,12 +114,4 @@ public class DynamicDataSourceProperties implements InitializingBean { this.primary = primary; } - public Map getTargetDataSources() { - return targetDataSources; - } - - public void setTargetDataSources(Map targetDataSources) { - this.targetDataSources = targetDataSources; - } - } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SqlSessionFactoryConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SqlSessionFactoryConfig.java index 6fb3827..58ba309 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SqlSessionFactoryConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SqlSessionFactoryConfig.java @@ -11,8 +11,8 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; -import com.atomikos.util.IntraVmObjectRegistry; import com.ruoyi.common.service.mybatis.CreateSqlSessionFactory; +import com.ruoyi.framework.datasource.DataSourceManagement; import com.ruoyi.framework.datasource.DynamicSqlSessionTemplate; @Configuration @@ -24,18 +24,16 @@ public class SqlSessionFactoryConfig { @Autowired DynamicDataSourceProperties dataSourceProperties; + @Autowired + DataSourceManagement dataSourceManagement; + @Bean(name = "sqlSessionTemplate") public DynamicSqlSessionTemplate sqlSessionTemplate(Environment env) throws Exception { Map sqlSessionFactoryMap = new HashMap<>(); - Map targetDataSources = dataSourceProperties.getTargetDataSources(); + Map targetDataSources = dataSourceManagement.getDataSourcesMap(); for (Map.Entry entry : targetDataSources.entrySet()) { SqlSessionFactory sessionFactory = createSqlSessionFactory.createSqlSessionFactory(env, entry.getValue()); sqlSessionFactoryMap.put(entry.getKey(), sessionFactory); - // 应对热重载的特殊处理 - Object ret = com.atomikos.icatch.config.Configuration.removeResource(entry.getKey()); - if (ret != null) { - IntraVmObjectRegistry.removeResource(entry.getKey()); - } } SqlSessionFactory factoryMaster = sqlSessionFactoryMap.get(dataSourceProperties.getPrimary()); if (factoryMaster == null) { diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DataSourceManagement.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DataSourceManagement.java new file mode 100644 index 0000000..0538e64 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DataSourceManagement.java @@ -0,0 +1,60 @@ +package com.ruoyi.framework.datasource; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import javax.sql.DataSource; + +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Component; + +import com.ruoyi.common.service.datasource.CreateDataSource; +import com.ruoyi.framework.config.DynamicDataSourceProperties; + +@Component +public class DataSourceManagement implements InitializingBean { + private Map targetDataSources = new HashMap<>(); + + @Autowired + private DynamicDataSourceProperties dataSourceProperties; + + @Autowired + private CreateDataSource c; + + @Bean(name = "dynamicDataSource") + @Primary + public DynamicDataSource dataSource() { + Map objectMap = new HashMap<>(); + Map targetDataSources = this.getDataSourcesMap(); + for (Map.Entry entry : targetDataSources.entrySet()) { + objectMap.put(entry.getKey(), entry.getValue()); + } + return new DynamicDataSource(targetDataSources.get(dataSourceProperties.getPrimary()), objectMap); + } + + @Override + public void afterPropertiesSet() throws Exception { + dataSourceProperties.getDatasource() + .forEach((name, props) -> this.putDataSource(name, c.createDataSource(name, props))); + } + + public DataSource getDataSource(String name) { + return targetDataSources.get(name); + } + + public Collection getDataSources() { + return targetDataSources.values(); + } + + public Map getDataSourcesMap() { + return targetDataSources; + } + + public void putDataSource(String name, DataSource dataSource) { + targetDataSources.put(name, dataSource); + } +}