添加分布式事务
This commit is contained in:
parent
bb846dd269
commit
d7c89ef7a4
@ -1,11 +1,11 @@
|
||||
# MyBatis配置
|
||||
# mybatis:
|
||||
# # 搜索指定包别名
|
||||
# typeAliasesPackage: com.ruoyi.**.domain
|
||||
# # 配置mapper的扫描,找到所有的mapper.xml映射文件
|
||||
# mapperLocations: classpath*:mapper/**/*Mapper.xml
|
||||
# # 加载全局的配置文件
|
||||
# configLocation: classpath:mybatis/mybatis-config.xml
|
||||
mybatis:
|
||||
# 搜索指定包别名
|
||||
typeAliasesPackage: com.ruoyi.**.domain
|
||||
# 配置mapper的扫描,找到所有的mapper.xml映射文件
|
||||
mapperLocations: classpath*:mapper/**/*Mapper.xml
|
||||
# 加载全局的配置文件
|
||||
configLocation: classpath:mybatis/mybatis-config.xml
|
||||
|
||||
# PageHelper分页插件
|
||||
pagehelper:
|
||||
|
@ -40,6 +40,18 @@
|
||||
<artifactId>sharding-jdbc-core</artifactId>
|
||||
</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>
|
||||
<groupId>pro.fessional</groupId>
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.ruoyi.framework.config;
|
||||
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@ -16,7 +17,7 @@ import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||
// 表示通过aop框架暴露该代理对象,AopContext能够访问
|
||||
@EnableAspectJAutoProxy(exposeProxy = true)
|
||||
// 指定要扫描的Mapper类的包的路径
|
||||
@MapperScan("com.ruoyi.**.mapper")
|
||||
@MapperScan(basePackages = "com.ruoyi.**.mapper", sqlSessionTemplateRef = "sqlSessionTemplate")
|
||||
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.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.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.pool.DruidDataSource;
|
||||
import com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceBuilder;
|
||||
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;
|
||||
@ -40,27 +43,69 @@ 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;
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
@DependsOn({ "transactionManager" })
|
||||
@ConfigurationProperties("spring.datasource.druid.master")
|
||||
public DataSource masterDataSource(DruidProperties druidProperties) {
|
||||
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
|
||||
return druidProperties.dataSource(dataSource);
|
||||
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(DruidProperties druidProperties) {
|
||||
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
|
||||
return druidProperties.dataSource(dataSource);
|
||||
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;
|
||||
}
|
||||
|
||||
@Bean(name = "dynamicDataSource")
|
||||
@Primary
|
||||
public DynamicDataSource dataSource(DataSource masterDataSource) {
|
||||
Map<Object, Object> targetDataSources = new HashMap<>();
|
||||
targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);
|
||||
setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");
|
||||
targetDataSources.put(MASTER, masterDataSource);
|
||||
setDataSource(targetDataSources, SLAVE, "slaveDataSource");
|
||||
setDataSource(targetDataSources, DataSourceType.SHARDING.name(), "shardingDataSource");
|
||||
return new DynamicDataSource(masterDataSource, targetDataSources);
|
||||
}
|
||||
|
@ -3,8 +3,10 @@ package com.ruoyi.framework.config;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
@ -12,10 +14,13 @@ import org.apache.ibatis.io.VFS;
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
import org.mybatis.spring.SqlSessionFactoryBean;
|
||||
import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.slf4j.Logger;
|
||||
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.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
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.util.ClassUtils;
|
||||
|
||||
import com.ruoyi.common.enums.DataSourceType;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.common.utils.spring.SpringUtils;
|
||||
import com.ruoyi.framework.datasource.DynamicSqlSessionTemplate;
|
||||
|
||||
/**
|
||||
* Mybatis支持*匹配扫描包
|
||||
@ -34,14 +42,12 @@ import com.ruoyi.common.utils.StringUtils;
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnProperty(prefix = "mybatis", name = { "typeAliasesPackage", "mapperLocations",
|
||||
"configLocation" }, matchIfMissing = false)
|
||||
public class MyBatisConfig {
|
||||
@Autowired
|
||||
private Environment env;
|
||||
|
||||
static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
|
||||
|
||||
Logger logger = LoggerFactory.getLogger(MyBatisConfig.class);
|
||||
|
||||
public static String setTypeAliasesPackage(String typeAliasesPackage) {
|
||||
ResourcePatternResolver resolver = (ResourcePatternResolver) new PathMatchingResourcePatternResolver();
|
||||
MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver);
|
||||
@ -100,8 +106,7 @@ public class MyBatisConfig {
|
||||
return resources.toArray(new Resource[resources.size()]);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
|
||||
public SqlSessionFactory createSqlSessionFactory(Environment env, DataSource dataSource) throws Exception {
|
||||
String typeAliasesPackage = env.getProperty("mybatis.typeAliasesPackage");
|
||||
String mapperLocations = env.getProperty("mybatis.mapperLocations");
|
||||
String configLocation = env.getProperty("mybatis.configLocation");
|
||||
@ -115,4 +120,39 @@ public class MyBatisConfig {
|
||||
sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
|
||||
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.context.annotation.Configuration;
|
||||
|
||||
import com.alibaba.druid.pool.DruidDataSource;
|
||||
|
||||
/**
|
||||
@ -10,8 +11,7 @@ import com.alibaba.druid.pool.DruidDataSource;
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Configuration
|
||||
public class DruidProperties
|
||||
{
|
||||
public class DruidProperties {
|
||||
@Value("${spring.datasource.druid.initialSize}")
|
||||
private int initialSize;
|
||||
|
||||
@ -51,8 +51,7 @@ public class DruidProperties
|
||||
@Value("${spring.datasource.druid.testOnReturn}")
|
||||
private boolean testOnReturn;
|
||||
|
||||
public DruidDataSource dataSource(DruidDataSource datasource)
|
||||
{
|
||||
public DruidDataSource dataSource(DruidDataSource datasource) {
|
||||
/** 配置初始化大小、最小、最大 */
|
||||
datasource.setInitialSize(initialSize);
|
||||
datasource.setMaxActive(maxActive);
|
||||
@ -60,10 +59,10 @@ public class DruidProperties
|
||||
|
||||
/** 配置获取连接等待超时的时间 */
|
||||
datasource.setMaxWait(maxWait);
|
||||
|
||||
|
||||
/** 配置驱动连接超时时间,检测数据库建立连接的超时时间,单位是毫秒 */
|
||||
datasource.setConnectTimeout(connectTimeout);
|
||||
|
||||
|
||||
/** 配置网络超时时间,等待数据库操作完成的网络超时时间,单位是毫秒 */
|
||||
datasource.setSocketTimeout(socketTimeout);
|
||||
|
||||
@ -75,10 +74,13 @@ public class DruidProperties
|
||||
datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis);
|
||||
|
||||
/**
|
||||
* 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。
|
||||
* 用来检测连接是否有效的sql,要求是一个查询语句,常用select
|
||||
* 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。
|
||||
*/
|
||||
datasource.setValidationQuery(validationQuery);
|
||||
/** 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 */
|
||||
/**
|
||||
* 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
|
||||
*/
|
||||
datasource.setTestWhileIdle(testWhileIdle);
|
||||
/** 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */
|
||||
datasource.setTestOnBorrow(testOnBorrow);
|
||||
@ -86,4 +88,108 @@ public class DruidProperties
|
||||
datasource.setTestOnReturn(testOnReturn);
|
||||
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