springboot+atomikos+mybatis+druid,分布式事务(事务中切换数据源),支持Mysql+SQLServer

本文基于以下多位大神的成果进行调整,在此感谢。

参考文章:https://www.cnblogs.com/zhaojiatao/p/8407276.html

参考文章:https://blog.csdn.net/yy417168602/article/details/89015752

本项目采用Mybatis的XML方式,非注解方式

1.数据库配置文件src\main\resources\application.properties

spring.jmx.default-domain=项目名称
spring.http.encoding.force=true
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
server.tomcat.uri-encoding=UTF-8
spring.http.multipart.maxFileSize=100Mb
spring.http.multipart.maxRequestSize=1000Mb
spring.datasource.type=com.alibaba.druid.pool.xa.DruidXADataSource


mybatis.config-locations=classpath:mybatis-config.xml
mybatis.mapper-locations=classpath:mybatis/*.xml
mybatis.type-aliases-package=model

#mysql
sql1.datasource.name=数据库标识需唯一不能重复
sql1.datasource.url=jdbc:mysql://localhost:3306/数据库名称?allowMultiQueries=true&characterEncoding=utf-8
sql1.datasource.username=root
sql1.datasource.password=密码
sql1.datasource.driver-class-name=com.mysql.jdbc.Driver
sql1.datasource.test-on-borrow=false
sql1.datasource.test-while-idle=true
sql1.datasource.time-between-eviction-runs-millis=3600000

sql1.datasource.initialSize= 5
sql1.datasource.minIdle=5
sql1.datasource.maxActive=20
        # 配置获取连接等待超时的时间
sql1.datasource.maxWait=60000
        # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
sql1.datasource.timeBetweenEvictionRunsMillis=60000
        # 配置一个连接在池中最小生存的时间,单位是毫秒
sql1.datasource.minEvictableIdleTimeMillis=30
sql1.datasource.validationQuery=SELECT 1
sql1.datasource.validationQueryTimeout=10000
sql1.datasource.testWhileIdle=true
sql1.datasource.testOnBorrow=false
sql1.datasource.testOnReturn=false
        # 打开PSCache,并且指定每个连接上PSCache的大小
sql1.datasource.poolPreparedStatements=true
sql1.datasource.maxPoolPreparedStatementPerConnectionSize= 20


#sql server
sql3.datasource.name=数据库标识需唯一不能重复
sql3.datasource.url=jdbc:sqlserver://127.0.0.1:1433;databaseName=数据库名称
sql3.datasource.username=sa
sql3.datasource.password=密码
sql3.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver
sql3.datasource.test-on-borrow=false
sql3.datasource.test-while-idle=true
sql3.datasource.time-between-eviction-runs-millis=3600000

sql3.datasource.initialSize= 5
sql3.datasource.minIdle=5
sql3.datasource.maxActive=20
        # 配置获取连接等待超时的时间
sql3.datasource.maxWait=60000
        # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
sql3.datasource.timeBetweenEvictionRunsMillis=60000
        # 配置一个连接在池中最小生存的时间,单位是毫秒
sql3.datasource.minEvictableIdleTimeMillis=30
sql3.datasource.validationQuery=SELECT 1
sql3.datasource.validationQueryTimeout=10000
sql3.datasource.testWhileIdle=true
sql3.datasource.testOnBorrow=false
sql3.datasource.testOnReturn=false
        # 打开PSCache,并且指定每个连接上PSCache的大小
sql3.datasource.poolPreparedStatements=true
sql3.datasource.maxPoolPreparedStatementPerConnectionSize= 20

#sql server
sql4.datasource.name=数据库标识需唯一不能重复
sql4.datasource.url=jdbc:sqlserver://127.0.0.1:1433;databaseName=数据库名称
sql4.datasource.username=sa
sql4.datasource.password=密码
sql4.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver
sql4.datasource.test-on-borrow=false
sql4.datasource.test-while-idle=true
sql4.datasource.time-between-eviction-runs-millis=3600000

sql4.datasource.initialSize= 5
sql4.datasource.minIdle=5
sql4.datasource.maxActive=20
        # 配置获取连接等待超时的时间
sql4.datasource.maxWait=60000
        # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
sql4.datasource.timeBetweenEvictionRunsMillis=60000
        # 配置一个连接在池中最小生存的时间,单位是毫秒
sql4.datasource.minEvictableIdleTimeMillis=30
sql4.datasource.validationQuery=SELECT 1
sql4.datasource.validationQueryTimeout=10000
sql4.datasource.testWhileIdle=true
sql4.datasource.testOnBorrow=false
sql4.datasource.testOnReturn=false
        # 打开PSCache,并且指定每个连接上PSCache的大小
sql4.datasource.poolPreparedStatements=true
sql4.datasource.maxPoolPreparedStatementPerConnectionSize= 20

2.SpringBoot启动项,

package Application;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.env.Environment;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.annotation.EnableTransactionManagement;


@EnableTransactionManagement(proxyTargetClass = true)
@EnableAutoConfiguration
@EnableScheduling
@ComponentScan(basePackages={"controller","service","dao","baseUtils","common","model"})//包含配置信息的包一定要包含在内
public class StartApplication  extends SpringBootServletInitializer {
	@Autowired
    private Environment env;
	
	@Override  
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {  
        return application.sources(StartApplication.class);  
    }  
	
	public StartApplication(){    
		super();   
	    setRegisterErrorPageFilter(false);
	}
	
	public static void main(String[] args) {
		SpringApplication.run(StartApplication.class, args);
	}

}

3.多数据源配置文件:

AbstractDataSourceConfig
package baseUtils;

import com.atomikos.jdbc.AtomikosDataSourceBean;
import com.microsoft.sqlserver.jdbc.SQLServerXADataSource;
import org.springframework.core.env.Environment;

import javax.sql.DataSource;
import java.util.Properties;

public abstract class AbstractDataSourceConfig {


    protected DataSource getDataSource(Environment env,String dataSourceName) {
        AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
        Properties prop = new Properties();
        String prefix=dataSourceName+".datasource.";
        if(env.getProperty(prefix + "driver-class-name").equals("com.microsoft.sqlserver.jdbc.SQLServerDriver")){
            SQLServerXADataSource datasource = new SQLServerXADataSource();
            datasource.setURL(env.getProperty(prefix + "url"));
            datasource.setUser(env.getProperty(prefix + "username"));
            datasource.setPassword(env.getProperty(prefix + "password"));
            ds.setXaDataSource(datasource);
        }else {
            prop = build(env, prefix);
            ds.setXaDataSourceClassName("com.alibaba.druid.pool.xa.DruidXADataSource");
            ds.setPoolSize(5);
            ds.setXaProperties(prop);
        }
        ds.setUniqueResourceName("sql1");
        return ds;
    }

    private 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("driverClassName", env.getProperty(prefix + "driver-class-name", ""));
        prop.put("initialSize", env.getProperty(prefix + "initialSize", Integer.class));
        prop.put("maxActive", env.getProperty(prefix + "maxActive", Integer.class));
        prop.put("minIdle", env.getProperty(prefix + "minIdle", Integer.class));
        prop.put("maxWait", env.getProperty(prefix + "maxWait", Integer.class));
        prop.put("poolPreparedStatements", env.getProperty(prefix + "poolPreparedStatements", Boolean.class));

        prop.put("maxPoolPreparedStatementPerConnectionSize",
                env.getProperty(prefix + "maxPoolPreparedStatementPerConnectionSize", Integer.class));

        prop.put("maxPoolPreparedStatementPerConnectionSize",
                env.getProperty(prefix + "maxPoolPreparedStatementPerConnectionSize", Integer.class));

        prop.put("validationQuery", env.getProperty(prefix + "validationQuery"));

        prop.put("validationQueryTimeout", env.getProperty(prefix + "validationQueryTimeout", Integer.class));

        prop.put("testOnBorrow", env.getProperty(prefix + "testOnBorrow", Boolean.class));

        prop.put("testOnReturn", env.getProperty(prefix + "testOnReturn", Boolean.class));

        prop.put("testWhileIdle", env.getProperty(prefix + "testWhileIdle", Boolean.class));

        prop.put("timeBetweenEvictionRunsMillis",
                env.getProperty(prefix + "timeBetweenEvictionRunsMillis", Integer.class));

        prop.put("minEvictableIdleTimeMillis", env.getProperty(prefix + "minEvictableIdleTimeMillis", Integer.class));

        return prop;
    }
}
MybatisConfig
package baseUtils;

import com.alibaba.druid.filter.Filter;
import com.alibaba.druid.wall.WallConfig;
import com.alibaba.druid.wall.WallFilter;
import com.microsoft.sqlserver.jdbc.SQLServerXADataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean;
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 org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.util.*;

@Configuration
@Component
@MapperScan(basePackages = MybatisConfig.BASE_PACKAGE, sqlSessionTemplateRef = "sql1SessionTemplate")
public class MybatisConfig extends AbstractDataSourceConfig {
	@Autowired
    private Environment env;
    //mapper模式下的接口层
    static final String BASE_PACKAGE = "dao";//mapper包

    @Primary
    @Bean(name = "sql1")
    public DataSource dataSourceOne(Environment env) {
        return getDataSource(env,"sql1");    //主数据库
    }

    @Bean(name = "sql1SessionFactory")
    public SqlSessionFactory sqlSessionFactoryOne(@Qualifier("sql1") DataSource dataSource)
            throws Exception {
        return createSqlSessionFactory(dataSource);
    }


    @Bean(name = "sql1SessionTemplate")
    public CustomSqlSessionTemplate sql1SessionTemplate(@Qualifier("sql1SessionFactory") SqlSessionFactory factorySystem) throws Exception {
        Map<Object, SqlSessionFactory> sqlSessionFactoryMap = new HashMap<>();
        sqlSessionFactoryMap.put("sql1", factorySystem);
        CustomSqlSessionTemplate customSqlSessionTemplate = new CustomSqlSessionTemplate(factorySystem);
        customSqlSessionTemplate.setTargetSqlSessionFactorys(sqlSessionFactoryMap);
        return customSqlSessionTemplate;
    }

    /**
     * 创建数据源
     *
     * @param dataSource
     * @return
     */
    public SqlSessionFactory createSqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setTypeAliasesPackage(env.getProperty("dao"));// 指定基包
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        bean.setMapperLocations(resolver.getResources("classpath:/mybatis/*.xml"));
        return bean.getObject();
    }

    public org.apache.ibatis.session.Configuration configuration() {
        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        configuration.setMapUnderscoreToCamelCase(true);
        return configuration;
    }

    public DataSource getDataSource(String dataSourceName) {

        AtomikosDataSourceBean ds = new AtomikosDataSourceBean();

        Properties prop = new Properties();
        String prefix=dataSourceName+".datasource.";
        if(env.getProperty(prefix + "driver-class-name").equals("com.microsoft.sqlserver.jdbc.SQLServerDriver")){
            SQLServerXADataSource datasource = new SQLServerXADataSource();
            datasource.setURL(env.getProperty(prefix + "url"));
            datasource.setUser(env.getProperty(prefix + "username"));
            datasource.setPassword(env.getProperty(prefix + "password"));
            ds.setXaDataSource(datasource);
        }else {
            prop = build(env, prefix);
            ds.setXaDataSourceClassName("com.alibaba.druid.pool.xa.DruidXADataSource");
            ds.setPoolSize(5);
            ds.setXaProperties(prop);
        }
        ds.setUniqueResourceName(dataSourceName);
        return ds;
    }

    public 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("driverClassName", env.getProperty(prefix + "driver-class-name", ""));
        prop.put("initialSize", env.getProperty(prefix + "initialSize", Integer.class));
        prop.put("maxActive", env.getProperty(prefix + "maxActive", Integer.class));
        prop.put("minIdle", env.getProperty(prefix + "minIdle", Integer.class));
        prop.put("maxWait", env.getProperty(prefix + "maxWait", Integer.class));
        prop.put("poolPreparedStatements", env.getProperty(prefix + "poolPreparedStatements", Boolean.class));

        prop.put("maxPoolPreparedStatementPerConnectionSize",
                env.getProperty(prefix + "maxPoolPreparedStatementPerConnectionSize", Integer.class));

        prop.put("maxPoolPreparedStatementPerConnectionSize",
                env.getProperty(prefix + "maxPoolPreparedStatementPerConnectionSize", Integer.class));

        prop.put("validationQuery", env.getProperty(prefix + "validationQuery"));

        prop.put("validationQueryTimeout", env.getProperty(prefix + "validationQueryTimeout", Integer.class));

        prop.put("testOnBorrow", env.getProperty(prefix + "testOnBorrow", Boolean.class));

        prop.put("testOnReturn", env.getProperty(prefix + "testOnReturn", Boolean.class));

        prop.put("testWhileIdle", env.getProperty(prefix + "testWhileIdle", Boolean.class));

        prop.put("timeBetweenEvictionRunsMillis",
                env.getProperty(prefix + "timeBetweenEvictionRunsMillis", Integer.class));

        prop.put("minEvictableIdleTimeMillis", env.getProperty(prefix + "minEvictableIdleTimeMillis", Integer.class));

        return prop;
    }

}
CustomSqlSessionTemplate
package baseUtils;

import static java.lang.reflect.Proxy.newProxyInstance;
import static org.apache.ibatis.reflection.ExceptionUtil.unwrapThrowable;
import static org.mybatis.spring.SqlSessionUtils.closeSqlSession;
import static org.mybatis.spring.SqlSessionUtils.getSqlSession;
import static org.mybatis.spring.SqlSessionUtils.isSqlSessionTransactional;


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;
import org.springframework.util.Assert;


public class CustomSqlSessionTemplate 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 void setTargetSqlSessionFactorys(Map<Object, SqlSessionFactory> targetSqlSessionFactorys) {
        this.targetSqlSessionFactorys = targetSqlSessionFactorys;
    }

    public Map<Object, SqlSessionFactory> getTargetSqlSessionFactorys() {
        return targetSqlSessionFactorys;
    }

    public void setDefaultTargetSqlSessionFactory(SqlSessionFactory defaultTargetSqlSessionFactory) {
        this.defaultTargetSqlSessionFactory = defaultTargetSqlSessionFactory;
    }

    public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
    }

    public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
        this(sqlSessionFactory, executorType, new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration()
                .getEnvironment().getDataSource(), true));
    }

    public CustomSqlSessionTemplate(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;
    }

    @Override
    public SqlSessionFactory getSqlSessionFactory() {
        SqlSessionFactory targetSqlSessionFactory = targetSqlSessionFactorys.get(DataSourceContextHolder.getDatasourceType());
        if (targetSqlSessionFactory != null) {
            return targetSqlSessionFactory;
        } else if (defaultTargetSqlSessionFactory != null) {
            return defaultTargetSqlSessionFactory;
        } else {
            Assert.notNull(targetSqlSessionFactorys, "Property 'targetSqlSessionFactorys' or 'defaultTargetSqlSessionFactory' are required");
            Assert.notNull(defaultTargetSqlSessionFactory, "Property 'defaultTargetSqlSessionFactory' or 'targetSqlSessionFactorys' are required");
        }
        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}
     */
    public void select(String statement, ResultHandler handler) {
        this.sqlSessionProxy.select(statement, handler);
    }

    /**
     * {@inheritDoc}
     */
    public void select(String statement, Object parameter, ResultHandler handler) {
        this.sqlSessionProxy.select(statement, parameter, handler);
    }

    /**
     * {@inheritDoc}
     */
    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(
                    CustomSqlSessionTemplate.this.getSqlSessionFactory(),
                    CustomSqlSessionTemplate.this.executorType,
                    CustomSqlSessionTemplate.this.exceptionTranslator);
            try {
                Object result = method.invoke(sqlSession, args);
                if (!isSqlSessionTransactional(sqlSession, CustomSqlSessionTemplate.this.getSqlSessionFactory())) {
                    // force commit even on non-dirty sessions because some databases require
                    // a commit/rollback before calling close()
                    sqlSession.commit(true);
                }
                return result;
            } catch (Throwable t) {
                Throwable unwrapped = unwrapThrowable(t);
                if (CustomSqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
                    Throwable translated = CustomSqlSessionTemplate.this.exceptionTranslator
                            .translateExceptionIfPossible((PersistenceException) unwrapped);
                    if (translated != null) {
                        unwrapped = translated;
                    }
                }
                throw unwrapped;
            } finally {
                closeSqlSession(sqlSession, CustomSqlSessionTemplate.this.getSqlSessionFactory());
            }
        }
    }

}
DataSourceContextHolder
package baseUtils;

import java.util.ArrayList;
import java.util.List;

//数据库上下文切换
public class DataSourceContextHolder {
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

    public static List<String> dataSourceKeys = new ArrayList<>();

    /**
     * 设置当前数据库。
     *
     * @param dbType
     */
    public static void setDatasourceType(String dbType) {
        contextHolder.set(dbType);

    }

    public static void setDefaultDataSource() {
    }

    /**
     * 取得当前数据源。
     *
     * @return
     */
    public static String getDatasourceType() {
        String str = contextHolder.get();
        return str;
    }

    /**
     * 清除上下文数据
     */
    public static void clearDatasourceType() {
        contextHolder.remove();
    }

    /**
     * @param dataSourceId
     * @return
     */
    public static boolean containsDataSource(String dataSourceId) {
        return dataSourceKeys.contains(dataSourceId);
    }

}
DataSourceManager
package baseUtils;

import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;

@Component
public class DataSourceManager {
    @Autowired
    private CustomSqlSessionTemplate sqlSessionTemplate;

    @Autowired
    private MybatisConfig mybatisConfig;

    /**
     * 初始或者重载入数据源
     *
     * @throws Exception
     */
    @PostConstruct
    public void loadDataSource() throws Exception {
        String[] dbArr={"sql3","sql4"};//不包括主数据库
        Map<Object, SqlSessionFactory> newSqlSessionFactoryMap = new HashMap<>();
        Map<Object, SqlSessionFactory> sqlSessionFactoryMap = sqlSessionTemplate.getTargetSqlSessionFactorys();
        for(int i=0;i<dbArr.length;i++){
            SqlSessionFactory sqlSessionFactory = mybatisConfig.createSqlSessionFactory(mybatisConfig.getDataSource(dbArr[i]));
            newSqlSessionFactoryMap.put(dbArr[i], sqlSessionFactory);
        }
        newSqlSessionFactoryMap.putAll(sqlSessionFactoryMap);
        this.sqlSessionTemplate.setTargetSqlSessionFactorys(newSqlSessionFactoryMap);
    }

}

4.AOP切换数据源

DataSourceAspect
package baseUtils;

import baseUtils.DataSourceContextHolder;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import service.BaiseService;
import service.OAService;

@Aspect
@Component
public class DataSourceAspect {
	 /**
     * 使用空方法定义切点表达式
     */
    @Pointcut("execution(* service.*.*(..))")	//配置包
    public void declareJointPointExpression() {
    }

    /**
     * 使用定义切点表达式的方法进行切点表达式的引入
     */
    @Before("declareJointPointExpression()")
    public void setDataSourceKey(JoinPoint point) {
        if (point.getTarget() instanceof 指定Service1) {
            DataSourceContextHolder.setDatasourceType("sql4");
        }else if (point.getTarget() instanceof Service2) {
            DataSourceContextHolder.setDatasourceType("sql3");
        }else {
            DataSourceContextHolder.setDatasourceType("sql1");    //其他采用默认数据库
        }
    }

}

5.事务中切换数据源,service代码片段


@Service("testService")
public class testService {

//测试事务中切换数据源,分布式事务
 @Transactional(rollbackFor = Exception.class)    //注意这一行,没有rollbackFor会报错
    public int test(){
		DataSourceContextHolder.setDatasourceType("sql1");    //切换数据源
    	adminMapper.AddAdmin(77,"test","test","test",198);    //数据库1的插入操作
		DataSourceContextHolder.setDatasourceType("sql3");     //切换数据源
		baiseMapper.CRMlockUser(1,"test");                    //数据库3的更新操作
    	System.out.println(1/0);    //注释则成功执行,不注释测报错回滚
    	return 1;
    }
}

6.使用SQLServer做为数据源时,会碰到一些问题,例如不支持SQLServer的提示,函数 RECOVER: 失败。状态为: -3等。请参考Atomikos XA分布式事务支持SQLServer的配置操作

7.如果批量更新报错:java.sql.SQLException: sql injection violation, multi-statement not allow 

请去除关于filters的配置,采用默认配置。Mysql配置的url中请追加参数allowMultiQueries=true

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值