1. 全局异常捕获错误问题修复

2. 多数据源允许仅修改配置文件即可追加更多数据源,且与dynamic-datasource使用方式相似,并追加字符串指定数据源的方式
3. 多数据源加分布式事务时从数据源无法被druid监控到问题修复
This commit is contained in:
Dftre 2024-07-01 03:49:00 +08:00
parent ee2c9c511d
commit 5a16b39164
9 changed files with 359 additions and 162 deletions

View File

@ -0,0 +1,149 @@
<mxfile host="65bd71144e">
<diagram id="27K8aLRXgDd5Ry5UGMpc" name="第 1 页">
<mxGraphModel dx="2298" dy="760" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="7" value="" style="edgeStyle=none;html=1;" parent="1" source="2" target="4" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="2" value="数据源1" style="html=1;" parent="1" vertex="1">
<mxGeometry x="510" y="340" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="8" value="" style="edgeStyle=none;html=1;" parent="1" source="3" target="5" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="3" value="数据源2" style="html=1;" parent="1" vertex="1">
<mxGeometry x="-90" y="340" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="9" style="edgeStyle=none;html=1;" parent="1" source="4" target="28" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="571.4814814814818" y="470" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="44" style="edgeStyle=none;html=1;" parent="1" source="4" target="43" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="4" value="&lt;div style=&quot;background-color: rgb(255, 255, 255); font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 14px; line-height: 19px;&quot;&gt;DataSource1&lt;/div&gt;" style="html=1;" parent="1" vertex="1">
<mxGeometry x="325" y="340" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="10" style="edgeStyle=none;html=1;" parent="1" source="5" target="28" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="556.25" y="470" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="45" style="edgeStyle=none;html=1;" parent="1" source="5" target="43" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="5" value="&lt;div style=&quot;background-color: rgb(255, 255, 255); font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 14px; line-height: 19px;&quot;&gt;DataSource2&lt;/div&gt;" style="html=1;" parent="1" vertex="1">
<mxGeometry x="100" y="340" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="15" value="" style="edgeStyle=none;html=1;" parent="1" source="11" target="28" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="636.25" y="470" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="11" value="&lt;div style=&quot;background-color: rgb(255, 255, 255); font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 14px; line-height: 19px;&quot;&gt;&lt;div style=&quot;line-height: 19px;&quot;&gt;DynamicDataSourceContextHolder&lt;/div&gt;&lt;/div&gt;" style="html=1;" parent="1" vertex="1">
<mxGeometry x="460" y="460" width="270" height="50" as="geometry"/>
</mxCell>
<mxCell id="13" value="&lt;div style=&quot;background-color: rgb(255, 255, 255); font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 14px; line-height: 19px;&quot;&gt;&lt;h1 style=&quot;box-sizing: border-box; outline: 0px; margin: 0px; padding: 0px; font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Microsoft YaHei&amp;quot;, SimHei, Arial, SimSun; font-size: 28px; overflow-wrap: break-word; color: rgb(34, 34, 38); word-break: break-all; font-variant-ligatures: common-ligatures; text-align: start;&quot; id=&quot;articleContentId&quot; class=&quot;title-article&quot;&gt;Spring AbstractRoutingDataSource&lt;br&gt;&lt;br&gt;&lt;/h1&gt;&lt;div&gt;* determineCurrentLookupKey&lt;/div&gt;&lt;/div&gt;" style="html=1;" parent="1" vertex="1">
<mxGeometry x="-380" y="455" width="520" height="70" as="geometry"/>
</mxCell>
<mxCell id="19" value="" style="edgeStyle=none;html=1;" parent="1" source="16" target="18" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="16" value="方法1" style="html=1;" parent="1" vertex="1">
<mxGeometry x="880" y="340" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="20" value="" style="edgeStyle=none;html=1;" parent="1" source="17" target="18" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="17" value="方法2" style="html=1;" parent="1" vertex="1">
<mxGeometry x="880" y="580" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="21" value="" style="edgeStyle=none;html=1;" parent="1" source="18" target="11" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="18" value="&lt;div style=&quot;background-color: rgb(255, 255, 255); font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 14px; line-height: 19px;&quot;&gt;DataSourceAspect&lt;/div&gt;" style="html=1;" parent="1" vertex="1">
<mxGeometry x="790" y="460" width="290" height="50" as="geometry"/>
</mxCell>
<mxCell id="22" value="" style="edgeStyle=none;html=1;" parent="1" source="23" target="26" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="23" value="数据源1" style="html=1;" parent="1" vertex="1">
<mxGeometry x="-90" y="580" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="24" value="" style="edgeStyle=none;html=1;" parent="1" source="25" target="27" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="25" value="数据源2" style="html=1;" parent="1" vertex="1">
<mxGeometry x="510" y="580" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="29" style="edgeStyle=none;html=1;" parent="1" source="26" target="28" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="46" style="edgeStyle=none;html=1;" parent="1" source="26" target="47" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="240" y="790" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="26" value="&lt;div style=&quot;background-color: rgb(255, 255, 255); font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 14px; line-height: 19px;&quot;&gt;&lt;div style=&quot;line-height: 19px;&quot;&gt;AtomikosDataSourceBean1&lt;/div&gt;&lt;/div&gt;" style="html=1;" parent="1" vertex="1">
<mxGeometry x="60" y="580" width="190" height="50" as="geometry"/>
</mxCell>
<mxCell id="30" style="edgeStyle=none;html=1;" parent="1" source="27" target="28" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="49" style="edgeStyle=none;html=1;" parent="1" source="27" target="48" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="27" value="&lt;div style=&quot;background-color: rgb(255, 255, 255); font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 14px; line-height: 19px;&quot;&gt;&lt;div style=&quot;line-height: 19px;&quot;&gt;AtomikosDataSourceBean2&lt;/div&gt;&lt;/div&gt;" style="html=1;" parent="1" vertex="1">
<mxGeometry x="285" y="580" width="190" height="50" as="geometry"/>
</mxCell>
<mxCell id="59" style="edgeStyle=none;html=1;" parent="1" source="28" target="13" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="28" value="&lt;div style=&quot;background-color: rgb(255, 255, 255); font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 14px; line-height: 19px;&quot;&gt;DynamicDataSource&lt;/div&gt;" style="whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="170" y="455" width="230" height="60" as="geometry"/>
</mxCell>
<mxCell id="54" value="" style="edgeStyle=none;html=1;" parent="1" source="43" target="53" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="43" value="&lt;div style=&quot;background-color: rgb(255, 255, 255); font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 14px; line-height: 19px;&quot;&gt;sqlSessionFactory&lt;/div&gt;" style="whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="160" y="210" width="210" height="60" as="geometry"/>
</mxCell>
<mxCell id="51" style="edgeStyle=none;html=1;" parent="1" source="47" target="50" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="47" value="&lt;div style=&quot;background-color: rgb(255, 255, 255); font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 14px; line-height: 19px;&quot;&gt;SqlSessionFactory1&lt;/div&gt;" style="html=1;" parent="1" vertex="1">
<mxGeometry x="55" y="670" width="200" height="50" as="geometry"/>
</mxCell>
<mxCell id="52" style="edgeStyle=none;html=1;" parent="1" source="48" target="50" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="48" value="&lt;div style=&quot;background-color: rgb(255, 255, 255); font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 14px; line-height: 19px;&quot;&gt;SqlSessionFactory2&lt;/div&gt;" style="whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="280" y="670" width="200" height="50" as="geometry"/>
</mxCell>
<mxCell id="56" style="edgeStyle=none;html=1;" parent="1" source="50" target="57" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="-593.9655172413791" y="670" as="targetPoint"/>
<Array as="points">
<mxPoint x="-470" y="815"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="50" value="&lt;div style=&quot;background-color: rgb(255, 255, 255); font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 14px; line-height: 19px;&quot;&gt;DynamicSqlSessionTemplate&lt;/div&gt;" style="whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="160" y="790" width="210" height="50" as="geometry"/>
</mxCell>
<mxCell id="58" style="edgeStyle=none;html=1;" parent="1" source="53" target="57" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="53" value="&lt;div style=&quot;background-color: rgb(255, 255, 255); font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 14px; line-height: 19px;&quot;&gt;&lt;div style=&quot;line-height: 19px;&quot;&gt;SqlSessionTemplate&lt;/div&gt;&lt;/div&gt;" style="whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="-573" y="210" width="210" height="60" as="geometry"/>
</mxCell>
<mxCell id="57" value="mybatis" style="html=1;" parent="1" vertex="1">
<mxGeometry x="-523" y="465" width="110" height="50" as="geometry"/>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

View File

@ -3,20 +3,20 @@ spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
druid:
dynamic:
primary: MASTER
datasource:
# 主库数据源
master:
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
# 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:
# 初始连接数
initialSize: 5
# 最小连接池数量

View File

@ -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 "";
}

View File

@ -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))
{
if (StringUtils.isNotNull(dataSource)) {
if ("".equals(dataSource.name())) {
DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name());
} else {
DynamicDataSourceContextHolder.setDataSourceType(dataSource.name());
}
try
{
return point.proceed();
}
finally
{
try {
return point.proceed();
} 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;
}

View File

@ -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<Object, Object> 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<Object, Object> 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<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);
}
/**

View File

@ -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<String, DataSourceProperties> datasource;
private String primary;
private Map<String, DataSource> 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<String, DataSourceProperties> getDatasource() {
return datasource;
}
public void setDatasource(Map<String, DataSourceProperties> datasource) {
this.datasource = datasource;
}
public String getPrimary() {
return primary;
}
public void setPrimary(String primary) {
this.primary = primary;
}
public Map<String, DataSource> getTargetDataSources() {
return targetDataSources;
}
public void setTargetDataSources(Map<String, DataSource> targetDataSources) {
this.targetDataSources = targetDataSources;
}
}

View File

@ -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<Object, SqlSessionFactory> 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<Object, SqlSessionFactory> sqlSessionFactoryMap) {
try {
SqlSessionFactory factorySlave = SpringUtils.getBean(sqlSessionFactoryName);
sqlSessionFactoryMap.put(dataSourceType.name(), factorySlave);
} catch (Exception e) {
logger.error("Failed to register a SqlSessionFactory:{}", sqlSessionFactoryName);
}
}
}

View File

@ -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<Object, SqlSessionFactory> sqlSessionFactoryMap = new HashMap<>();
Map<String, DataSource> targetDataSources = dataSourceProperties.getTargetDataSources();
for (Map.Entry<String, DataSource> 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;
}
}

View File

@ -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;
/**
* 全局异常处理器
*