解耦atomikos
This commit is contained in:
parent
ac40c0f92f
commit
85992058e2
@ -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);
|
||||||
|
}
|
@ -1,5 +1,8 @@
|
|||||||
package com.ruoyi.framework.config;
|
package com.ruoyi.framework.config;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
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.context.annotation.DependsOn;
|
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.UserTransactionImp;
|
||||||
import com.atomikos.icatch.jta.UserTransactionManager;
|
import com.atomikos.icatch.jta.UserTransactionManager;
|
||||||
|
import com.atomikos.jdbc.AtomikosDataSourceBean;
|
||||||
|
|
||||||
|
import jakarta.annotation.PreDestroy;
|
||||||
import jakarta.transaction.TransactionManager;
|
import jakarta.transaction.TransactionManager;
|
||||||
import jakarta.transaction.UserTransaction;
|
import jakarta.transaction.UserTransaction;
|
||||||
|
|
||||||
@ -18,11 +23,9 @@ import jakarta.transaction.UserTransaction;
|
|||||||
* @author ruoyi
|
* @author ruoyi
|
||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
public class AtomikosConfig
|
public class AtomikosConfig {
|
||||||
{
|
|
||||||
@Bean(name = "userTransaction")
|
@Bean(name = "userTransaction")
|
||||||
public UserTransaction userTransaction() throws Throwable
|
public UserTransaction userTransaction() throws Throwable {
|
||||||
{
|
|
||||||
UserTransaction userTransaction = new UserTransactionImp();
|
UserTransaction userTransaction = new UserTransactionImp();
|
||||||
// 设置事务超时时间为10000毫秒
|
// 设置事务超时时间为10000毫秒
|
||||||
userTransaction.setTransactionTimeout(10000);
|
userTransaction.setTransactionTimeout(10000);
|
||||||
@ -30,8 +33,7 @@ public class AtomikosConfig
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close")
|
@Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close")
|
||||||
public TransactionManager atomikosTransactionManager() throws Throwable
|
public TransactionManager atomikosTransactionManager() throws Throwable {
|
||||||
{
|
|
||||||
UserTransactionManager userTransactionManager = new UserTransactionManager();
|
UserTransactionManager userTransactionManager = new UserTransactionManager();
|
||||||
// 设置是否强制关闭事务管理器为false
|
// 设置是否强制关闭事务管理器为false
|
||||||
userTransactionManager.setForceShutdown(false);
|
userTransactionManager.setForceShutdown(false);
|
||||||
@ -40,10 +42,22 @@ public class AtomikosConfig
|
|||||||
|
|
||||||
@Bean(name = "transactionManager")
|
@Bean(name = "transactionManager")
|
||||||
@DependsOn({ "userTransaction", "atomikosTransactionManager" })
|
@DependsOn({ "userTransaction", "atomikosTransactionManager" })
|
||||||
public PlatformTransactionManager transactionManager() throws Throwable
|
public PlatformTransactionManager transactionManager() throws Throwable {
|
||||||
{
|
|
||||||
UserTransaction userTransaction = userTransaction();
|
UserTransaction userTransaction = userTransaction();
|
||||||
TransactionManager atomikosTransactionManager = atomikosTransactionManager();
|
TransactionManager atomikosTransactionManager = atomikosTransactionManager();
|
||||||
return new JtaTransactionManager(userTransaction, atomikosTransactionManager);
|
return new JtaTransactionManager(userTransaction, atomikosTransactionManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<AtomikosDataSourceBean> atomikosDataSourceBeans = new ArrayList<>();
|
||||||
|
|
||||||
|
public List<AtomikosDataSourceBean> getAtomikosDataSourceBeans() {
|
||||||
|
return atomikosDataSourceBeans;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreDestroy
|
||||||
|
public void destroy() {
|
||||||
|
for (AtomikosDataSourceBean aDataSourceBean : this.atomikosDataSourceBeans) {
|
||||||
|
aDataSourceBean.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,8 @@
|
|||||||
package com.ruoyi.framework.config;
|
package com.ruoyi.framework.config;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.ArrayList;
|
||||||
import java.util.Map;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
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.boot.web.servlet.FilterRegistrationBean;
|
||||||
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.context.annotation.Primary;
|
|
||||||
|
|
||||||
|
import com.alibaba.druid.pool.DruidDataSource;
|
||||||
import com.alibaba.druid.spring.boot3.autoconfigure.properties.DruidStatProperties;
|
import com.alibaba.druid.spring.boot3.autoconfigure.properties.DruidStatProperties;
|
||||||
import com.alibaba.druid.util.Utils;
|
import com.alibaba.druid.util.Utils;
|
||||||
import com.ruoyi.framework.datasource.DynamicDataSource;
|
|
||||||
|
|
||||||
|
import jakarta.annotation.PreDestroy;
|
||||||
import jakarta.servlet.Filter;
|
import jakarta.servlet.Filter;
|
||||||
import jakarta.servlet.FilterChain;
|
import jakarta.servlet.FilterChain;
|
||||||
import jakarta.servlet.ServletException;
|
import jakarta.servlet.ServletException;
|
||||||
@ -36,18 +34,9 @@ public class DruidConfig {
|
|||||||
@Autowired
|
@Autowired
|
||||||
DynamicDataSourceProperties dataSourceProperties;
|
DynamicDataSourceProperties dataSourceProperties;
|
||||||
|
|
||||||
Logger logger = LoggerFactory.getLogger(DruidConfig.class);
|
private List<DruidDataSource> druidDataSources = new ArrayList<>();
|
||||||
|
|
||||||
@Bean(name = "dynamicDataSource")
|
Logger logger = LoggerFactory.getLogger(DruidConfig.class);
|
||||||
@Primary
|
|
||||||
public DynamicDataSource dataSource() {
|
|
||||||
Map<Object, Object> objectMap = new HashMap<>();
|
|
||||||
Map<String, DataSource> targetDataSources = dataSourceProperties.getTargetDataSources();
|
|
||||||
for (Map.Entry<String, DataSource> entry : targetDataSources.entrySet()) {
|
|
||||||
objectMap.put(entry.getKey(), entry.getValue());
|
|
||||||
}
|
|
||||||
return new DynamicDataSource(targetDataSources.get(dataSourceProperties.getPrimary()), objectMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 去除监控页面底部的广告
|
* 去除监控页面底部的广告
|
||||||
@ -91,4 +80,15 @@ public class DruidConfig {
|
|||||||
registrationBean.addUrlPatterns(commonJsPattern);
|
registrationBean.addUrlPatterns(commonJsPattern);
|
||||||
return registrationBean;
|
return registrationBean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<DruidDataSource> getDruidDataSources() {
|
||||||
|
return druidDataSources;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreDestroy
|
||||||
|
public void destroy() {
|
||||||
|
for (DruidDataSource druidDataSource : druidDataSources) {
|
||||||
|
druidDataSource.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,56 +4,27 @@ import java.sql.Connection;
|
|||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.Statement;
|
import java.sql.Statement;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
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.autoconfigure.jdbc.DataSourceProperties;
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.DependsOn;
|
|
||||||
|
|
||||||
import com.alibaba.druid.pool.DruidDataSource;
|
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.common.utils.spring.SpringUtils;
|
||||||
import com.ruoyi.framework.config.properties.DruidProperties;
|
import com.ruoyi.framework.config.properties.DruidProperties;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@DependsOn({ "transactionManager" })
|
|
||||||
@ConfigurationProperties(prefix = "spring.datasource.dynamic")
|
@ConfigurationProperties(prefix = "spring.datasource.dynamic")
|
||||||
public class DynamicDataSourceProperties implements InitializingBean {
|
public class DynamicDataSourceProperties {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceProperties.class);
|
|
||||||
private Map<String, DataSourceProperties> datasource;
|
private Map<String, DataSourceProperties> datasource;
|
||||||
private String primary;
|
private String primary;
|
||||||
private Map<String, DataSource> targetDataSources = new HashMap<>();
|
|
||||||
|
|
||||||
@Override
|
public void validateDataSource(DataSource dataSource) {
|
||||||
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) {
|
|
||||||
try (Connection conn = dataSource.getConnection()) {
|
try (Connection conn = dataSource.getConnection()) {
|
||||||
String validationQuery = "SELECT 1";
|
String validationQuery = "SELECT 1";
|
||||||
try (Statement stmt = conn.createStatement();
|
try (Statement stmt = conn.createStatement();
|
||||||
@ -143,12 +114,4 @@ public class DynamicDataSourceProperties implements InitializingBean {
|
|||||||
this.primary = primary;
|
this.primary = primary;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, DataSource> getTargetDataSources() {
|
|
||||||
return targetDataSources;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTargetDataSources(Map<String, DataSource> targetDataSources) {
|
|
||||||
this.targetDataSources = targetDataSources;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -11,8 +11,8 @@ import org.springframework.context.annotation.Bean;
|
|||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
|
|
||||||
import com.atomikos.util.IntraVmObjectRegistry;
|
|
||||||
import com.ruoyi.common.service.mybatis.CreateSqlSessionFactory;
|
import com.ruoyi.common.service.mybatis.CreateSqlSessionFactory;
|
||||||
|
import com.ruoyi.framework.datasource.DataSourceManagement;
|
||||||
import com.ruoyi.framework.datasource.DynamicSqlSessionTemplate;
|
import com.ruoyi.framework.datasource.DynamicSqlSessionTemplate;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@ -24,18 +24,16 @@ public class SqlSessionFactoryConfig {
|
|||||||
@Autowired
|
@Autowired
|
||||||
DynamicDataSourceProperties dataSourceProperties;
|
DynamicDataSourceProperties dataSourceProperties;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
DataSourceManagement dataSourceManagement;
|
||||||
|
|
||||||
@Bean(name = "sqlSessionTemplate")
|
@Bean(name = "sqlSessionTemplate")
|
||||||
public DynamicSqlSessionTemplate sqlSessionTemplate(Environment env) throws Exception {
|
public DynamicSqlSessionTemplate sqlSessionTemplate(Environment env) throws Exception {
|
||||||
Map<Object, SqlSessionFactory> sqlSessionFactoryMap = new HashMap<>();
|
Map<Object, SqlSessionFactory> sqlSessionFactoryMap = new HashMap<>();
|
||||||
Map<String, DataSource> targetDataSources = dataSourceProperties.getTargetDataSources();
|
Map<String, DataSource> targetDataSources = dataSourceManagement.getDataSourcesMap();
|
||||||
for (Map.Entry<String, DataSource> entry : targetDataSources.entrySet()) {
|
for (Map.Entry<String, DataSource> entry : targetDataSources.entrySet()) {
|
||||||
SqlSessionFactory sessionFactory = createSqlSessionFactory.createSqlSessionFactory(env, entry.getValue());
|
SqlSessionFactory sessionFactory = createSqlSessionFactory.createSqlSessionFactory(env, entry.getValue());
|
||||||
sqlSessionFactoryMap.put(entry.getKey(), sessionFactory);
|
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());
|
SqlSessionFactory factoryMaster = sqlSessionFactoryMap.get(dataSourceProperties.getPrimary());
|
||||||
if (factoryMaster == null) {
|
if (factoryMaster == null) {
|
||||||
|
@ -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<String, DataSource> targetDataSources = new HashMap<>();
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DynamicDataSourceProperties dataSourceProperties;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private CreateDataSource c;
|
||||||
|
|
||||||
|
@Bean(name = "dynamicDataSource")
|
||||||
|
@Primary
|
||||||
|
public DynamicDataSource dataSource() {
|
||||||
|
Map<Object, Object> objectMap = new HashMap<>();
|
||||||
|
Map<String, DataSource> targetDataSources = this.getDataSourcesMap();
|
||||||
|
for (Map.Entry<String, DataSource> 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<DataSource> getDataSources() {
|
||||||
|
return targetDataSources.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, DataSource> getDataSourcesMap() {
|
||||||
|
return targetDataSources;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putDataSource(String name, DataSource dataSource) {
|
||||||
|
targetDataSources.put(name, dataSource);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user