添加分布式事务
This commit is contained in:
parent
bb846dd269
commit
d7c89ef7a4
@ -1,11 +1,11 @@
|
|||||||
# MyBatis配置
|
# MyBatis配置
|
||||||
# mybatis:
|
mybatis:
|
||||||
# # 搜索指定包别名
|
# 搜索指定包别名
|
||||||
# typeAliasesPackage: com.ruoyi.**.domain
|
typeAliasesPackage: com.ruoyi.**.domain
|
||||||
# # 配置mapper的扫描,找到所有的mapper.xml映射文件
|
# 配置mapper的扫描,找到所有的mapper.xml映射文件
|
||||||
# mapperLocations: classpath*:mapper/**/*Mapper.xml
|
mapperLocations: classpath*:mapper/**/*Mapper.xml
|
||||||
# # 加载全局的配置文件
|
# 加载全局的配置文件
|
||||||
# configLocation: classpath:mybatis/mybatis-config.xml
|
configLocation: classpath:mybatis/mybatis-config.xml
|
||||||
|
|
||||||
# PageHelper分页插件
|
# PageHelper分页插件
|
||||||
pagehelper:
|
pagehelper:
|
||||||
|
@ -40,6 +40,18 @@
|
|||||||
<artifactId>sharding-jdbc-core</artifactId>
|
<artifactId>sharding-jdbc-core</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.atomikos</groupId>
|
||||||
|
<artifactId>transactions-spring-boot3-starter</artifactId>
|
||||||
|
<version>6.0.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.transaction</groupId>
|
||||||
|
<artifactId>jta</artifactId>
|
||||||
|
<version>1.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- 验证码 -->
|
<!-- 验证码 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>pro.fessional</groupId>
|
<groupId>pro.fessional</groupId>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.ruoyi.framework.config;
|
package com.ruoyi.framework.config;
|
||||||
|
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
|
||||||
import org.mybatis.spring.annotation.MapperScan;
|
import org.mybatis.spring.annotation.MapperScan;
|
||||||
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
|
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
@ -16,7 +17,7 @@ import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
|||||||
// 表示通过aop框架暴露该代理对象,AopContext能够访问
|
// 表示通过aop框架暴露该代理对象,AopContext能够访问
|
||||||
@EnableAspectJAutoProxy(exposeProxy = true)
|
@EnableAspectJAutoProxy(exposeProxy = true)
|
||||||
// 指定要扫描的Mapper类的包的路径
|
// 指定要扫描的Mapper类的包的路径
|
||||||
@MapperScan("com.ruoyi.**.mapper")
|
@MapperScan(basePackages = "com.ruoyi.**.mapper", sqlSessionTemplateRef = "sqlSessionTemplate")
|
||||||
public class ApplicationConfig
|
public class ApplicationConfig
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
package com.ruoyi.framework.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.DependsOn;
|
||||||
|
import org.springframework.transaction.PlatformTransactionManager;
|
||||||
|
import org.springframework.transaction.jta.JtaTransactionManager;
|
||||||
|
|
||||||
|
import com.atomikos.icatch.jta.UserTransactionImp;
|
||||||
|
import com.atomikos.icatch.jta.UserTransactionManager;
|
||||||
|
|
||||||
|
import jakarta.transaction.TransactionManager;
|
||||||
|
import jakarta.transaction.UserTransaction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JTA 事务配置
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class AtomikosConfig
|
||||||
|
{
|
||||||
|
@Bean(name = "userTransaction")
|
||||||
|
public UserTransaction userTransaction() throws Throwable
|
||||||
|
{
|
||||||
|
UserTransactionImp userTransactionImp = new UserTransactionImp();
|
||||||
|
userTransactionImp.setTransactionTimeout(10000);
|
||||||
|
return userTransactionImp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close")
|
||||||
|
public TransactionManager atomikosTransactionManager() throws Throwable
|
||||||
|
{
|
||||||
|
UserTransactionManager userTransactionManager = new UserTransactionManager();
|
||||||
|
userTransactionManager.setForceShutdown(false);
|
||||||
|
return userTransactionManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean(name = "transactionManager")
|
||||||
|
@DependsOn({ "userTransaction", "atomikosTransactionManager" })
|
||||||
|
public PlatformTransactionManager transactionManager() throws Throwable
|
||||||
|
{
|
||||||
|
UserTransaction userTransaction = userTransaction();
|
||||||
|
TransactionManager atomikosTransactionManager = atomikosTransactionManager();
|
||||||
|
return new JtaTransactionManager(userTransaction, atomikosTransactionManager);
|
||||||
|
}
|
||||||
|
}
|
@ -3,22 +3,25 @@ package com.ruoyi.framework.config;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
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.DependsOn;
|
||||||
import org.springframework.context.annotation.Primary;
|
import org.springframework.context.annotation.Primary;
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
|
|
||||||
import com.alibaba.druid.pool.DruidDataSource;
|
|
||||||
import com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceBuilder;
|
|
||||||
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.atomikos.jdbc.AtomikosDataSourceBean;
|
||||||
import com.ruoyi.common.enums.DataSourceType;
|
import com.ruoyi.common.enums.DataSourceType;
|
||||||
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;
|
||||||
@ -40,27 +43,69 @@ public class DruidConfig {
|
|||||||
|
|
||||||
Logger logger = LoggerFactory.getLogger(DruidConfig.class);
|
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;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
@Primary
|
||||||
|
@DependsOn({ "transactionManager" })
|
||||||
@ConfigurationProperties("spring.datasource.druid.master")
|
@ConfigurationProperties("spring.datasource.druid.master")
|
||||||
public DataSource masterDataSource(DruidProperties druidProperties) {
|
public DataSource masterDataSource(Environment env) {
|
||||||
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
|
String prefix = "spring.datasource.druid.master.";
|
||||||
return druidProperties.dataSource(dataSource);
|
return getDataSource(env, prefix, MASTER);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConfigurationProperties("spring.datasource.druid.slave")
|
@ConfigurationProperties("spring.datasource.druid.slave")
|
||||||
|
@DependsOn({ "transactionManager" })
|
||||||
@ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
|
@ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
|
||||||
public DataSource slaveDataSource(DruidProperties druidProperties) {
|
public DataSource slaveDataSource(Environment env) {
|
||||||
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
|
String prefix = "spring.datasource.druid.slave.";
|
||||||
return druidProperties.dataSource(dataSource);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean(name = "dynamicDataSource")
|
@Bean(name = "dynamicDataSource")
|
||||||
@Primary
|
@Primary
|
||||||
public DynamicDataSource dataSource(DataSource masterDataSource) {
|
public DynamicDataSource dataSource(DataSource masterDataSource) {
|
||||||
Map<Object, Object> targetDataSources = new HashMap<>();
|
Map<Object, Object> targetDataSources = new HashMap<>();
|
||||||
targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);
|
targetDataSources.put(MASTER, masterDataSource);
|
||||||
setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");
|
setDataSource(targetDataSources, SLAVE, "slaveDataSource");
|
||||||
setDataSource(targetDataSources, DataSourceType.SHARDING.name(), "shardingDataSource");
|
setDataSource(targetDataSources, DataSourceType.SHARDING.name(), "shardingDataSource");
|
||||||
return new DynamicDataSource(masterDataSource, targetDataSources);
|
return new DynamicDataSource(masterDataSource, targetDataSources);
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,10 @@ package com.ruoyi.framework.config;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
@ -12,10 +14,13 @@ import org.apache.ibatis.io.VFS;
|
|||||||
import org.apache.ibatis.session.SqlSessionFactory;
|
import org.apache.ibatis.session.SqlSessionFactory;
|
||||||
import org.mybatis.spring.SqlSessionFactoryBean;
|
import org.mybatis.spring.SqlSessionFactoryBean;
|
||||||
import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
|
import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.slf4j.Logger;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.slf4j.LoggerFactory;
|
||||||
|
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.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Primary;
|
||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
import org.springframework.core.io.DefaultResourceLoader;
|
import org.springframework.core.io.DefaultResourceLoader;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
@ -26,7 +31,10 @@ import org.springframework.core.type.classreading.MetadataReader;
|
|||||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
|
|
||||||
|
import com.ruoyi.common.enums.DataSourceType;
|
||||||
import com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
import com.ruoyi.common.utils.spring.SpringUtils;
|
||||||
|
import com.ruoyi.framework.datasource.DynamicSqlSessionTemplate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mybatis支持*匹配扫描包
|
* Mybatis支持*匹配扫描包
|
||||||
@ -34,14 +42,12 @@ import com.ruoyi.common.utils.StringUtils;
|
|||||||
* @author ruoyi
|
* @author ruoyi
|
||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConditionalOnProperty(prefix = "mybatis", name = { "typeAliasesPackage", "mapperLocations",
|
|
||||||
"configLocation" }, matchIfMissing = false)
|
|
||||||
public class MyBatisConfig {
|
public class MyBatisConfig {
|
||||||
@Autowired
|
|
||||||
private Environment env;
|
|
||||||
|
|
||||||
static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
|
static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
|
||||||
|
|
||||||
|
Logger logger = LoggerFactory.getLogger(MyBatisConfig.class);
|
||||||
|
|
||||||
public static String setTypeAliasesPackage(String typeAliasesPackage) {
|
public static String setTypeAliasesPackage(String typeAliasesPackage) {
|
||||||
ResourcePatternResolver resolver = (ResourcePatternResolver) new PathMatchingResourcePatternResolver();
|
ResourcePatternResolver resolver = (ResourcePatternResolver) new PathMatchingResourcePatternResolver();
|
||||||
MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver);
|
MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver);
|
||||||
@ -100,8 +106,7 @@ public class MyBatisConfig {
|
|||||||
return resources.toArray(new Resource[resources.size()]);
|
return resources.toArray(new Resource[resources.size()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
public SqlSessionFactory createSqlSessionFactory(Environment env, DataSource dataSource) throws Exception {
|
||||||
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
|
|
||||||
String typeAliasesPackage = env.getProperty("mybatis.typeAliasesPackage");
|
String typeAliasesPackage = env.getProperty("mybatis.typeAliasesPackage");
|
||||||
String mapperLocations = env.getProperty("mybatis.mapperLocations");
|
String mapperLocations = env.getProperty("mybatis.mapperLocations");
|
||||||
String configLocation = env.getProperty("mybatis.configLocation");
|
String configLocation = env.getProperty("mybatis.configLocation");
|
||||||
@ -115,4 +120,39 @@ public class MyBatisConfig {
|
|||||||
sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
|
sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
|
||||||
return sessionFactory.getObject();
|
return sessionFactory.getObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean(name = "sqlSessionFactoryMaster")
|
||||||
|
@Primary
|
||||||
|
public SqlSessionFactory sqlSessionFactoryMaster(Environment env,
|
||||||
|
@Qualifier("masterDataSource") DataSource dataSource) throws Exception {
|
||||||
|
return createSqlSessionFactory(env, dataSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean(name = "sqlSessionFactorySlave")
|
||||||
|
@ConditionalOnBean(name = "slaveDataSource")
|
||||||
|
public SqlSessionFactory sqlSessionFactorySlave(Environment env,
|
||||||
|
@Qualifier("slaveDataSource") DataSource dataSource) throws Exception {
|
||||||
|
return createSqlSessionFactory(env, dataSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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, factorySlave);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Failed to register a SqlSessionFactory:{}", sqlSessionFactoryName);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package com.ruoyi.framework.config.properties;
|
|||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
import com.alibaba.druid.pool.DruidDataSource;
|
import com.alibaba.druid.pool.DruidDataSource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -10,8 +11,7 @@ import com.alibaba.druid.pool.DruidDataSource;
|
|||||||
* @author ruoyi
|
* @author ruoyi
|
||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
public class DruidProperties
|
public class DruidProperties {
|
||||||
{
|
|
||||||
@Value("${spring.datasource.druid.initialSize}")
|
@Value("${spring.datasource.druid.initialSize}")
|
||||||
private int initialSize;
|
private int initialSize;
|
||||||
|
|
||||||
@ -51,8 +51,7 @@ public class DruidProperties
|
|||||||
@Value("${spring.datasource.druid.testOnReturn}")
|
@Value("${spring.datasource.druid.testOnReturn}")
|
||||||
private boolean testOnReturn;
|
private boolean testOnReturn;
|
||||||
|
|
||||||
public DruidDataSource dataSource(DruidDataSource datasource)
|
public DruidDataSource dataSource(DruidDataSource datasource) {
|
||||||
{
|
|
||||||
/** 配置初始化大小、最小、最大 */
|
/** 配置初始化大小、最小、最大 */
|
||||||
datasource.setInitialSize(initialSize);
|
datasource.setInitialSize(initialSize);
|
||||||
datasource.setMaxActive(maxActive);
|
datasource.setMaxActive(maxActive);
|
||||||
@ -75,10 +74,13 @@ public class DruidProperties
|
|||||||
datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis);
|
datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。
|
* 用来检测连接是否有效的sql,要求是一个查询语句,常用select
|
||||||
|
* 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。
|
||||||
*/
|
*/
|
||||||
datasource.setValidationQuery(validationQuery);
|
datasource.setValidationQuery(validationQuery);
|
||||||
/** 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 */
|
/**
|
||||||
|
* 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
|
||||||
|
*/
|
||||||
datasource.setTestWhileIdle(testWhileIdle);
|
datasource.setTestWhileIdle(testWhileIdle);
|
||||||
/** 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */
|
/** 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */
|
||||||
datasource.setTestOnBorrow(testOnBorrow);
|
datasource.setTestOnBorrow(testOnBorrow);
|
||||||
@ -86,4 +88,108 @@ public class DruidProperties
|
|||||||
datasource.setTestOnReturn(testOnReturn);
|
datasource.setTestOnReturn(testOnReturn);
|
||||||
return datasource;
|
return datasource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getInitialSize() {
|
||||||
|
return initialSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInitialSize(int initialSize) {
|
||||||
|
this.initialSize = initialSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMinIdle() {
|
||||||
|
return minIdle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMinIdle(int minIdle) {
|
||||||
|
this.minIdle = minIdle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxActive() {
|
||||||
|
return maxActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxActive(int maxActive) {
|
||||||
|
this.maxActive = maxActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxWait() {
|
||||||
|
return maxWait;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxWait(int maxWait) {
|
||||||
|
this.maxWait = maxWait;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getConnectTimeout() {
|
||||||
|
return connectTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConnectTimeout(int connectTimeout) {
|
||||||
|
this.connectTimeout = connectTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSocketTimeout() {
|
||||||
|
return socketTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSocketTimeout(int socketTimeout) {
|
||||||
|
this.socketTimeout = socketTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTimeBetweenEvictionRunsMillis() {
|
||||||
|
return timeBetweenEvictionRunsMillis;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTimeBetweenEvictionRunsMillis(int timeBetweenEvictionRunsMillis) {
|
||||||
|
this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMinEvictableIdleTimeMillis() {
|
||||||
|
return minEvictableIdleTimeMillis;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMinEvictableIdleTimeMillis(int minEvictableIdleTimeMillis) {
|
||||||
|
this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxEvictableIdleTimeMillis() {
|
||||||
|
return maxEvictableIdleTimeMillis;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxEvictableIdleTimeMillis(int maxEvictableIdleTimeMillis) {
|
||||||
|
this.maxEvictableIdleTimeMillis = maxEvictableIdleTimeMillis;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValidationQuery() {
|
||||||
|
return validationQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValidationQuery(String validationQuery) {
|
||||||
|
this.validationQuery = validationQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isTestWhileIdle() {
|
||||||
|
return testWhileIdle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTestWhileIdle(boolean testWhileIdle) {
|
||||||
|
this.testWhileIdle = testWhileIdle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isTestOnBorrow() {
|
||||||
|
return testOnBorrow;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTestOnBorrow(boolean testOnBorrow) {
|
||||||
|
this.testOnBorrow = testOnBorrow;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isTestOnReturn() {
|
||||||
|
return testOnReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTestOnReturn(boolean testOnReturn) {
|
||||||
|
this.testOnReturn = testOnReturn;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,358 @@
|
|||||||
|
package com.ruoyi.framework.datasource;
|
||||||
|
|
||||||
|
import static java.lang.reflect.Proxy.*;
|
||||||
|
import static org.apache.ibatis.reflection.ExceptionUtil.*;
|
||||||
|
import static org.mybatis.spring.SqlSessionUtils.*;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationHandler;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.ibatis.exceptions.PersistenceException;
|
||||||
|
import org.apache.ibatis.executor.BatchResult;
|
||||||
|
import org.apache.ibatis.session.Configuration;
|
||||||
|
import org.apache.ibatis.session.ExecutorType;
|
||||||
|
import org.apache.ibatis.session.ResultHandler;
|
||||||
|
import org.apache.ibatis.session.RowBounds;
|
||||||
|
import org.apache.ibatis.session.SqlSession;
|
||||||
|
import org.apache.ibatis.session.SqlSessionFactory;
|
||||||
|
import org.mybatis.spring.MyBatisExceptionTranslator;
|
||||||
|
import org.mybatis.spring.SqlSessionTemplate;
|
||||||
|
import org.springframework.dao.support.PersistenceExceptionTranslator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义SqlSessionTemplate,动态切换数据源
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class DynamicSqlSessionTemplate extends SqlSessionTemplate
|
||||||
|
{
|
||||||
|
private final SqlSessionFactory sqlSessionFactory;
|
||||||
|
private final ExecutorType executorType;
|
||||||
|
private final SqlSession sqlSessionProxy;
|
||||||
|
private final PersistenceExceptionTranslator exceptionTranslator;
|
||||||
|
private Map<Object, SqlSessionFactory> targetSqlSessionFactorys;
|
||||||
|
private SqlSessionFactory defaultTargetSqlSessionFactory;
|
||||||
|
|
||||||
|
public DynamicSqlSessionTemplate(SqlSessionFactory sqlSessionFactory)
|
||||||
|
{
|
||||||
|
this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
|
||||||
|
}
|
||||||
|
|
||||||
|
public DynamicSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType)
|
||||||
|
{
|
||||||
|
this(sqlSessionFactory, executorType, new MyBatisExceptionTranslator(
|
||||||
|
sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public DynamicSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
|
||||||
|
PersistenceExceptionTranslator exceptionTranslator)
|
||||||
|
{
|
||||||
|
super(sqlSessionFactory, executorType, exceptionTranslator);
|
||||||
|
this.sqlSessionFactory = sqlSessionFactory;
|
||||||
|
this.executorType = executorType;
|
||||||
|
this.exceptionTranslator = exceptionTranslator;
|
||||||
|
this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),
|
||||||
|
new Class[] { SqlSession.class }, new SqlSessionInterceptor());
|
||||||
|
this.defaultTargetSqlSessionFactory = sqlSessionFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTargetSqlSessionFactorys(Map<Object, SqlSessionFactory> targetSqlSessionFactorys)
|
||||||
|
{
|
||||||
|
this.targetSqlSessionFactorys = targetSqlSessionFactorys;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDefaultTargetSqlSessionFactory(SqlSessionFactory defaultTargetSqlSessionFactory)
|
||||||
|
{
|
||||||
|
this.defaultTargetSqlSessionFactory = defaultTargetSqlSessionFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqlSessionFactory getSqlSessionFactory()
|
||||||
|
{
|
||||||
|
SqlSessionFactory targetSqlSessionFactory = targetSqlSessionFactorys
|
||||||
|
.get(DynamicDataSourceContextHolder.getDataSourceType());
|
||||||
|
if (targetSqlSessionFactory != null)
|
||||||
|
{
|
||||||
|
return targetSqlSessionFactory;
|
||||||
|
}
|
||||||
|
else if (defaultTargetSqlSessionFactory != null)
|
||||||
|
{
|
||||||
|
return defaultTargetSqlSessionFactory;
|
||||||
|
}
|
||||||
|
return this.sqlSessionFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Configuration getConfiguration()
|
||||||
|
{
|
||||||
|
return this.getSqlSessionFactory().getConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExecutorType getExecutorType()
|
||||||
|
{
|
||||||
|
return this.executorType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PersistenceExceptionTranslator getPersistenceExceptionTranslator()
|
||||||
|
{
|
||||||
|
return this.exceptionTranslator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public <T> T selectOne(String statement)
|
||||||
|
{
|
||||||
|
return this.sqlSessionProxy.<T> selectOne(statement);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public <T> T selectOne(String statement, Object parameter)
|
||||||
|
{
|
||||||
|
return this.sqlSessionProxy.<T> selectOne(statement, parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public <K, V> Map<K, V> selectMap(String statement, String mapKey)
|
||||||
|
{
|
||||||
|
return this.sqlSessionProxy.<K, V> selectMap(statement, mapKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey)
|
||||||
|
{
|
||||||
|
return this.sqlSessionProxy.<K, V> selectMap(statement, parameter, mapKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds)
|
||||||
|
{
|
||||||
|
return this.sqlSessionProxy.<K, V> selectMap(statement, parameter, mapKey, rowBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public <E> List<E> selectList(String statement)
|
||||||
|
{
|
||||||
|
return this.sqlSessionProxy.<E> selectList(statement);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public <E> List<E> selectList(String statement, Object parameter)
|
||||||
|
{
|
||||||
|
return this.sqlSessionProxy.<E> selectList(statement, parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds)
|
||||||
|
{
|
||||||
|
return this.sqlSessionProxy.<E> selectList(statement, parameter, rowBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
public void select(String statement, ResultHandler handler)
|
||||||
|
{
|
||||||
|
this.sqlSessionProxy.select(statement, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
public void select(String statement, Object parameter, ResultHandler handler)
|
||||||
|
{
|
||||||
|
this.sqlSessionProxy.select(statement, parameter, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler)
|
||||||
|
{
|
||||||
|
this.sqlSessionProxy.select(statement, parameter, rowBounds, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public int insert(String statement)
|
||||||
|
{
|
||||||
|
return this.sqlSessionProxy.insert(statement);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public int insert(String statement, Object parameter)
|
||||||
|
{
|
||||||
|
return this.sqlSessionProxy.insert(statement, parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public int update(String statement)
|
||||||
|
{
|
||||||
|
return this.sqlSessionProxy.update(statement);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public int update(String statement, Object parameter)
|
||||||
|
{
|
||||||
|
return this.sqlSessionProxy.update(statement, parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public int delete(String statement)
|
||||||
|
{
|
||||||
|
return this.sqlSessionProxy.delete(statement);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public int delete(String statement, Object parameter)
|
||||||
|
{
|
||||||
|
return this.sqlSessionProxy.delete(statement, parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public <T> T getMapper(Class<T> type)
|
||||||
|
{
|
||||||
|
return getConfiguration().getMapper(type, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public void commit()
|
||||||
|
{
|
||||||
|
throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public void commit(boolean force)
|
||||||
|
{
|
||||||
|
throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public void rollback()
|
||||||
|
{
|
||||||
|
throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public void rollback(boolean force)
|
||||||
|
{
|
||||||
|
throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public void close()
|
||||||
|
{
|
||||||
|
throw new UnsupportedOperationException("Manual close is not allowed over a Spring managed SqlSession");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public void clearCache()
|
||||||
|
{
|
||||||
|
this.sqlSessionProxy.clearCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public Connection getConnection()
|
||||||
|
{
|
||||||
|
return this.sqlSessionProxy.getConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* @since 1.0.2
|
||||||
|
*/
|
||||||
|
public List<BatchResult> flushStatements()
|
||||||
|
{
|
||||||
|
return this.sqlSessionProxy.flushStatements();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxy needed to route MyBatis method calls to the proper SqlSession got from Spring's Transaction Manager It also
|
||||||
|
* unwraps exceptions thrown by {@code Method#invoke(Object, Object...)} to pass a {@code PersistenceException} to
|
||||||
|
* the {@code PersistenceExceptionTranslator}.
|
||||||
|
*/
|
||||||
|
private class SqlSessionInterceptor implements InvocationHandler
|
||||||
|
{
|
||||||
|
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
|
||||||
|
{
|
||||||
|
final SqlSession sqlSession = getSqlSession(DynamicSqlSessionTemplate.this.getSqlSessionFactory(),
|
||||||
|
DynamicSqlSessionTemplate.this.executorType, DynamicSqlSessionTemplate.this.exceptionTranslator);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Object result = method.invoke(sqlSession, args);
|
||||||
|
if (!isSqlSessionTransactional(sqlSession, DynamicSqlSessionTemplate.this.getSqlSessionFactory()))
|
||||||
|
{
|
||||||
|
sqlSession.commit(true);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (Throwable t)
|
||||||
|
{
|
||||||
|
Throwable unwrapped = unwrapThrowable(t);
|
||||||
|
if (DynamicSqlSessionTemplate.this.exceptionTranslator != null
|
||||||
|
&& unwrapped instanceof PersistenceException)
|
||||||
|
{
|
||||||
|
Throwable translated = DynamicSqlSessionTemplate.this.exceptionTranslator
|
||||||
|
.translateExceptionIfPossible((PersistenceException) unwrapped);
|
||||||
|
if (translated != null)
|
||||||
|
{
|
||||||
|
unwrapped = translated;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw unwrapped;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
closeSqlSession(sqlSession, DynamicSqlSessionTemplate.this.getSqlSessionFactory());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user