From 5a16b39164ab85a2ae56825df5be5c0cec615bab Mon Sep 17 00:00:00 2001 From: Dftre <3066417822@qq.com> Date: Mon, 1 Jul 2024 03:49:00 +0800 Subject: [PATCH] =?UTF-8?q?1.=20=E5=85=A8=E5=B1=80=E5=BC=82=E5=B8=B8?= =?UTF-8?q?=E6=8D=95=E8=8E=B7=E9=94=99=E8=AF=AF=E9=97=AE=E9=A2=98=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=202.=20=E5=A4=9A=E6=95=B0=E6=8D=AE=E6=BA=90=E5=85=81?= =?UTF-8?q?=E8=AE=B8=E4=BB=85=E4=BF=AE=E6=94=B9=E9=85=8D=E7=BD=AE=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E5=8D=B3=E5=8F=AF=E8=BF=BD=E5=8A=A0=E6=9B=B4=E5=A4=9A?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E6=BA=90=EF=BC=8C=E4=B8=94=E4=B8=8Edynamic-d?= =?UTF-8?q?atasource=E4=BD=BF=E7=94=A8=E6=96=B9=E5=BC=8F=E7=9B=B8=E4=BC=BC?= =?UTF-8?q?=EF=BC=8C=E5=B9=B6=E8=BF=BD=E5=8A=A0=E5=AD=97=E7=AC=A6=E4=B8=B2?= =?UTF-8?q?=E6=8C=87=E5=AE=9A=E6=95=B0=E6=8D=AE=E6=BA=90=E7=9A=84=E6=96=B9?= =?UTF-8?q?=E5=BC=8F=203.=20=E5=A4=9A=E6=95=B0=E6=8D=AE=E6=BA=90=E5=8A=A0?= =?UTF-8?q?=E5=88=86=E5=B8=83=E5=BC=8F=E4=BA=8B=E5=8A=A1=E6=97=B6=E4=BB=8E?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E6=BA=90=E6=97=A0=E6=B3=95=E8=A2=ABdruid?= =?UTF-8?q?=E7=9B=91=E6=8E=A7=E5=88=B0=E9=97=AE=E9=A2=98=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/关于多数据源与分布式事务.drawio | 149 ++++++++++++++++++ .../src/main/resources/application-druid.yml | 26 +-- .../ruoyi/common/annotation/DataSource.java | 12 +- .../framework/aspectj/DataSourceAspect.java | 34 ++-- .../ruoyi/framework/config/DruidConfig.java | 92 +---------- .../config/DynamicDataSourceProperties.java | 134 ++++++++++++++++ .../ruoyi/framework/config/MyBatisConfig.java | 28 ---- .../config/SqlSessionFactoryConfig.java | 42 +++-- .../web/exception/GlobalExceptionHandler.java | 4 +- 9 files changed, 359 insertions(+), 162 deletions(-) create mode 100644 doc/关于多数据源与分布式事务.drawio create mode 100644 ruoyi-framework/src/main/java/com/ruoyi/framework/config/DynamicDataSourceProperties.java diff --git a/doc/关于多数据源与分布式事务.drawio b/doc/关于多数据源与分布式事务.drawio new file mode 100644 index 0000000..54ac704 --- /dev/null +++ b/doc/关于多数据源与分布式事务.drawio @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/application-druid.yml b/ruoyi-admin/src/main/resources/application-druid.yml index 56ce492..6c5e4ae 100644 --- a/ruoyi-admin/src/main/resources/application-druid.yml +++ b/ruoyi-admin/src/main/resources/application-druid.yml @@ -3,20 +3,20 @@ spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.cj.jdbc.Driver + dynamic: + primary: MASTER + datasource: + # 主库数据源 + MASTER: + url: jdbc:mysql://127.0.0.1/ry?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 + username: root + password: 123456 + # 从库数据源 + # SLAVE: + # url: jdbc:mysql://127.0.0.1/ruoyi?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 + # username: root + # password: 123456 druid: - # 主库数据源 - master: - url: jdbc:mysql://127.0.0.1/ry?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 - username: root - password: 123456 - # 从库数据源 - slave: - # 从数据源开关/默认关闭 - enabled: false - url: jdbc:mysql://127.0.0.1/ruoyi?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 - username: root - password: 123456 - # 初始连接数 initialSize: 5 # 最小连接池数量 diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSource.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSource.java index 79cd191..39d9d30 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSource.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSource.java @@ -6,6 +6,7 @@ import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; + import com.ruoyi.common.enums.DataSourceType; /** @@ -19,10 +20,15 @@ import com.ruoyi.common.enums.DataSourceType; @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited -public @interface DataSource -{ +public @interface DataSource { + /** - * 切换数据源名称 + * 切换数据源名称 - 枚举方式 */ public DataSourceType value() default DataSourceType.MASTER; + + /** + * 切换数据源名称 - 字符串方式 + */ + public String name() default ""; } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java index 8c2c9f4..f551e67 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java @@ -1,6 +1,7 @@ package com.ruoyi.framework.aspectj; import java.util.Objects; + import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; @@ -11,6 +12,7 @@ import org.slf4j.LoggerFactory; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; + import com.ruoyi.common.annotation.DataSource; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.framework.datasource.DynamicDataSourceContextHolder; @@ -23,33 +25,31 @@ import com.ruoyi.framework.datasource.DynamicDataSourceContextHolder; @Aspect @Order(1) @Component -public class DataSourceAspect -{ +public class DataSourceAspect { protected Logger logger = LoggerFactory.getLogger(getClass()); @Pointcut("@annotation(com.ruoyi.common.annotation.DataSource)" + "|| @within(com.ruoyi.common.annotation.DataSource)") - public void dsPointCut() - { + public void dsPointCut() { } @Around("dsPointCut()") - public Object around(ProceedingJoinPoint point) throws Throwable - { + public Object around(ProceedingJoinPoint point) throws Throwable { DataSource dataSource = getDataSource(point); - if (StringUtils.isNotNull(dataSource)) - { - DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name()); + if (StringUtils.isNotNull(dataSource)) { + if ("".equals(dataSource.name())) { + DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name()); + } else { + DynamicDataSourceContextHolder.setDataSourceType(dataSource.name()); + } + } - try - { + try { return point.proceed(); - } - finally - { + } finally { // 销毁数据源 在执行方法之后 DynamicDataSourceContextHolder.clearDataSourceType(); } @@ -58,12 +58,10 @@ public class DataSourceAspect /** * 获取需要切换的数据源 */ - public DataSource getDataSource(ProceedingJoinPoint point) - { + public DataSource getDataSource(ProceedingJoinPoint point) { MethodSignature signature = (MethodSignature) point.getSignature(); DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class); - if (Objects.nonNull(dataSource)) - { + if (Objects.nonNull(dataSource)) { return dataSource; } 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 b8af262..41a79fd 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 @@ -3,7 +3,6 @@ package com.ruoyi.framework.config; import java.io.IOException; import java.util.HashMap; import java.util.Map; -import java.util.Properties; import javax.sql.DataSource; @@ -11,20 +10,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.DependsOn; import org.springframework.context.annotation.Primary; -import org.springframework.core.env.Environment; import com.alibaba.druid.spring.boot3.autoconfigure.properties.DruidStatProperties; import com.alibaba.druid.util.Utils; -import com.atomikos.jdbc.AtomikosDataSourceBean; -import com.ruoyi.common.enums.DataSourceType; -import com.ruoyi.common.utils.spring.SpringUtils; -import com.ruoyi.framework.config.properties.DruidProperties; import com.ruoyi.framework.datasource.DynamicDataSource; import jakarta.servlet.Filter; @@ -41,88 +33,20 @@ import jakarta.servlet.ServletResponse; @Configuration public class DruidConfig { - Logger logger = LoggerFactory.getLogger(DruidConfig.class); - - public static final String MASTER = DataSourceType.MASTER.name(); - - public static final String SLAVE = DataSourceType.SLAVE.name(); - @Autowired - private DruidProperties druidProperties; + DynamicDataSourceProperties dataSourceProperties; - @Bean - @Primary - @DependsOn({ "transactionManager" }) - @ConfigurationProperties("spring.datasource.druid.master") - public DataSource masterDataSource(Environment env) { - String prefix = "spring.datasource.druid.master."; - return getDataSource(env, prefix, MASTER); - } - - @Bean - @ConfigurationProperties("spring.datasource.druid.slave") - @DependsOn({ "transactionManager" }) - @ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true") - public DataSource slaveDataSource(Environment env) { - String prefix = "spring.datasource.druid.slave."; - return getDataSource(env, prefix, SLAVE); - } - - protected DataSource getDataSource(Environment env, String prefix, String dataSourceName) { - Properties prop = build(env, prefix); - AtomikosDataSourceBean ds = new AtomikosDataSourceBean(); - ds.setXaDataSourceClassName("com.alibaba.druid.pool.xa.DruidXADataSource"); - // 添加连接池限制 - ds.setMaxPoolSize(50); - ds.setMinPoolSize(5); - ds.setBorrowConnectionTimeout(60); - ds.setUniqueResourceName(dataSourceName); - ds.setXaProperties(prop); - return ds; - } - - protected Properties build(Environment env, String prefix) { - Properties prop = new Properties(); - prop.put("url", env.getProperty(prefix + "url")); - prop.put("username", env.getProperty(prefix + "username")); - prop.put("password", env.getProperty(prefix + "password")); - prop.put("initialSize", druidProperties.getInitialSize()); - prop.put("minIdle", druidProperties.getMinIdle()); - prop.put("maxActive", druidProperties.getMaxActive()); - prop.put("maxWait", druidProperties.getMaxWait()); - prop.put("timeBetweenEvictionRunsMillis", druidProperties.getTimeBetweenEvictionRunsMillis()); - prop.put("minEvictableIdleTimeMillis", druidProperties.getMinEvictableIdleTimeMillis()); - prop.put("maxEvictableIdleTimeMillis", druidProperties.getMaxEvictableIdleTimeMillis()); - prop.put("validationQuery", druidProperties.getValidationQuery()); - prop.put("testWhileIdle", druidProperties.isTestWhileIdle()); - prop.put("testOnBorrow", druidProperties.isTestOnBorrow()); - prop.put("testOnReturn", druidProperties.isTestOnReturn()); - return prop; - } + Logger logger = LoggerFactory.getLogger(DruidConfig.class); @Bean(name = "dynamicDataSource") @Primary - public DynamicDataSource dataSource(DataSource masterDataSource) { - Map targetDataSources = new HashMap<>(); - targetDataSources.put(MASTER, masterDataSource); - setDataSource(targetDataSources, SLAVE, "slaveDataSource"); - return new DynamicDataSource(masterDataSource, targetDataSources); - } - - /** - * 设置数据源 - * - * @param targetDataSources 备选数据源集合 - * @param sourceName 数据源名称 - * @param beanName bean名称 - */ - public void setDataSource(Map targetDataSources, String sourceName, String beanName) { - try { - DataSource dataSource = SpringUtils.getBean(beanName); - targetDataSources.put(sourceName, dataSource); - } catch (Exception e) { - logger.error("Failed to register a data source:{}", beanName); + 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); } /** 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 new file mode 100644 index 0000000..695b0c6 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/DynamicDataSourceProperties.java @@ -0,0 +1,134 @@ +package com.ruoyi.framework.config; + +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import javax.sql.DataSource; + +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 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); + AtomikosDataSourceBean ds = new AtomikosDataSourceBean(); + ds.setXaDataSourceClassName("com.alibaba.druid.pool.xa.DruidXADataSource"); + // 添加连接池限制 + ds.setMaxPoolSize(10); + ds.setMinPoolSize(3); + ds.setBorrowConnectionTimeout(60); + ds.setUniqueResourceName(name); + ds.setXaProperties(prop); + setProperties(dataSource, prop); + ds.setXaDataSource(dataSource); + return ds; + } + + protected Properties build(DataSourceProperties dataSourceProperties) { + Properties prop = new Properties(); + DruidProperties druidProperties = SpringUtils.getBean(DruidProperties.class); + prop.put("url", dataSourceProperties.getUrl()); + prop.put("username", dataSourceProperties.getUsername()); + prop.put("password", dataSourceProperties.getPassword()); + prop.put("initialSize", druidProperties.getInitialSize()); + prop.put("minIdle", druidProperties.getMinIdle()); + prop.put("maxActive", druidProperties.getMaxActive()); + prop.put("maxWait", druidProperties.getMaxWait()); + prop.put("timeBetweenEvictionRunsMillis", druidProperties.getTimeBetweenEvictionRunsMillis()); + prop.put("minEvictableIdleTimeMillis", druidProperties.getMinEvictableIdleTimeMillis()); + prop.put("maxEvictableIdleTimeMillis", druidProperties.getMaxEvictableIdleTimeMillis()); + prop.put("validationQuery", druidProperties.getValidationQuery()); + prop.put("testWhileIdle", druidProperties.isTestWhileIdle()); + prop.put("testOnBorrow", druidProperties.isTestOnBorrow()); + prop.put("testOnReturn", druidProperties.isTestOnReturn()); + return prop; + } + + protected void setProperties(DruidDataSource dataSource, Properties prop) { + dataSource.setUrl(prop.getProperty("url")); + dataSource.setUsername(prop.getProperty("username")); + dataSource.setPassword(prop.getProperty("password")); + if(prop.getProperty("initialSize") != null){ + dataSource.setInitialSize(Integer.parseInt(prop.getProperty("initialSize"))); + } + if(prop.getProperty("minIdle") != null){ + dataSource.setMinIdle(Integer.parseInt(prop.getProperty("minIdle"))); + } + if(prop.getProperty("maxActive") != null){ + dataSource.setMaxActive(Integer.parseInt(prop.getProperty("maxActive"))); + } + if(prop.getProperty("maxWait") != null){ + dataSource.setMaxWait(Long.parseLong(prop.getProperty("maxWait"))); + } + if(prop.getProperty("timeBetweenEvictionRunsMillis") != null){ + dataSource.setTimeBetweenEvictionRunsMillis(Long.parseLong(prop.getProperty("timeBetweenEvictionRunsMillis"))); + } + if(prop.getProperty("minEvictableIdleTimeMillis") != null){ + dataSource.setMinEvictableIdleTimeMillis(Long.parseLong(prop.getProperty("minEvictableIdleTimeMillis"))); + } + if(prop.getProperty("maxEvictableIdleTimeMillis") != null){ + dataSource.setMaxEvictableIdleTimeMillis(Long.parseLong(prop.getProperty("maxEvictableIdleTimeMillis"))); + } + if(prop.getProperty("validationQuery") != null){ + dataSource.setValidationQuery(prop.getProperty("validationQuery")); + } + if(prop.getProperty("testWhileIdle") != null){ + dataSource.setTestWhileIdle(Boolean.parseBoolean(prop.getProperty("testWhileIdle"))); + } + if(prop.getProperty("testOnBorrow") != null){ + dataSource.setTestOnBorrow(Boolean.parseBoolean(prop.getProperty("testOnBorrow"))); + } + if(prop.getProperty("testOnReturn") != null){ + dataSource.setTestOnReturn(Boolean.parseBoolean(prop.getProperty("testOnReturn"))); + } + } + + public Map getDatasource() { + return datasource; + } + + public void setDatasource(Map datasource) { + this.datasource = datasource; + } + + public String getPrimary() { + return primary; + } + + public void setPrimary(String primary) { + 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/MyBatisConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MyBatisConfig.java index 0e314a1..29c8269 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MyBatisConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MyBatisConfig.java @@ -1,8 +1,5 @@ package com.ruoyi.framework.config; -import java.util.HashMap; -import java.util.Map; - import javax.sql.DataSource; import org.apache.ibatis.io.VFS; @@ -11,19 +8,15 @@ import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.boot.autoconfigure.SpringBootVFS; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import org.springframework.core.io.DefaultResourceLoader; -import com.ruoyi.common.enums.DataSourceType; import com.ruoyi.common.interceptor.mybatis.CreateSqlSessionFactory; import com.ruoyi.common.utils.MybatisUtils; import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.common.utils.spring.SpringUtils; -import com.ruoyi.framework.datasource.DynamicSqlSessionTemplate; /** * Mybatis支持*匹配扫描包 @@ -57,25 +50,4 @@ public class MyBatisConfig { }; } - @Bean(name = "sqlSessionTemplate") - public DynamicSqlSessionTemplate sqlSessionTemplate( - @Qualifier("sqlSessionFactoryMaster") SqlSessionFactory factoryMaster) throws Exception { - Map sqlSessionFactoryMap = new HashMap<>(); - sqlSessionFactoryMap.put(DruidConfig.MASTER, factoryMaster); - putSqlSessionFactory("sqlSessionFactorySlave", DataSourceType.SLAVE, sqlSessionFactoryMap); - DynamicSqlSessionTemplate customSqlSessionTemplate = new DynamicSqlSessionTemplate(factoryMaster); - customSqlSessionTemplate.setTargetSqlSessionFactorys(sqlSessionFactoryMap); - return customSqlSessionTemplate; - } - - private void putSqlSessionFactory(String sqlSessionFactoryName, DataSourceType dataSourceType, - Map sqlSessionFactoryMap) { - try { - SqlSessionFactory factorySlave = SpringUtils.getBean(sqlSessionFactoryName); - sqlSessionFactoryMap.put(dataSourceType.name(), factorySlave); - } catch (Exception e) { - logger.error("Failed to register a SqlSessionFactory:{}", sqlSessionFactoryName); - } - } - } 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 da95649..5377749 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 @@ -1,17 +1,19 @@ package com.ruoyi.framework.config; +import java.util.HashMap; +import java.util.Map; + import javax.sql.DataSource; import org.apache.ibatis.session.SqlSessionFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; import org.springframework.core.env.Environment; +import com.atomikos.util.IntraVmObjectRegistry; import com.ruoyi.common.interceptor.mybatis.CreateSqlSessionFactory; +import com.ruoyi.framework.datasource.DynamicSqlSessionTemplate; @Configuration public class SqlSessionFactoryConfig { @@ -19,17 +21,29 @@ public class SqlSessionFactoryConfig { @Autowired CreateSqlSessionFactory createSqlSessionFactory; - @Bean(name = "sqlSessionFactoryMaster") - @Primary - public SqlSessionFactory sqlSessionFactoryMaster(Environment env, - @Qualifier("masterDataSource") DataSource dataSource) throws Exception { - return createSqlSessionFactory.createSqlSessionFactory(env, dataSource); - } + @Autowired + DynamicDataSourceProperties dataSourceProperties; - @Bean(name = "sqlSessionFactorySlave") - @ConditionalOnBean(name = "slaveDataSource") - public SqlSessionFactory sqlSessionFactorySlave(Environment env, - @Qualifier("slaveDataSource") DataSource dataSource) throws Exception { - return createSqlSessionFactory.createSqlSessionFactory(env, dataSource); + @Bean(name = "sqlSessionTemplate") + public DynamicSqlSessionTemplate sqlSessionTemplate(Environment env) throws Exception { + Map sqlSessionFactoryMap = new HashMap<>(); + Map targetDataSources = dataSourceProperties.getTargetDataSources(); + 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) { + throw new RuntimeException("找不到主库配置" + dataSourceProperties.getPrimary()); + } + DynamicSqlSessionTemplate customSqlSessionTemplate = new DynamicSqlSessionTemplate(factoryMaster); + customSqlSessionTemplate.setTargetSqlSessionFactorys(sqlSessionFactoryMap); + return customSqlSessionTemplate; } } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java index e42f800..2df2ed3 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java @@ -1,7 +1,5 @@ package com.ruoyi.framework.web.exception; -import javax.servlet.http.HttpServletRequest; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.access.AccessDeniedException; @@ -21,6 +19,8 @@ import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.html.EscapeUtil; +import jakarta.servlet.http.HttpServletRequest; + /** * 全局异常处理器 *