新增多数据源用例
							
								
								
									
										
											BIN
										
									
								
								pictures/multi-console.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 54 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								pictures/multi-db-show.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 13 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								pictures/multi-db1-show.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 11 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								pictures/multi-db1.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 12 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								pictures/multi-db2-show.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 11 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								pictures/multi-db2.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 12 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								pictures/multi-tran-fail.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 12 KiB  | 
							
								
								
									
										91
									
								
								spring-boot/springboot-druid-mybatis-multi/pom.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,91 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 | 
			
		||||
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 | 
			
		||||
    <modelVersion>4.0.0</modelVersion>
 | 
			
		||||
    <parent>
 | 
			
		||||
        <groupId>org.springframework.boot</groupId>
 | 
			
		||||
        <artifactId>spring-boot-starter-parent</artifactId>
 | 
			
		||||
        <version>2.1.2.RELEASE</version>
 | 
			
		||||
        <relativePath/> <!-- lookup parent from repository -->
 | 
			
		||||
    </parent>
 | 
			
		||||
    <groupId>com.heibaiying</groupId>
 | 
			
		||||
    <artifactId>springboot-druid-mybatis-multi</artifactId>
 | 
			
		||||
    <version>0.0.1-SNAPSHOT</version>
 | 
			
		||||
    <name>springBoot-druid-mybatis-multi</name>
 | 
			
		||||
    <description>Demo project for Spring Boot</description>
 | 
			
		||||
 | 
			
		||||
    <properties>
 | 
			
		||||
        <java.version>1.8</java.version>
 | 
			
		||||
    </properties>
 | 
			
		||||
 | 
			
		||||
    <dependencies>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>org.mybatis.spring.boot</groupId>
 | 
			
		||||
            <artifactId>mybatis-spring-boot-starter</artifactId>
 | 
			
		||||
            <version>1.3.2</version>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>org.mybatis</groupId>
 | 
			
		||||
            <artifactId>mybatis</artifactId>
 | 
			
		||||
            <version>3.4.6</version>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>org.mybatis</groupId>
 | 
			
		||||
            <artifactId>mybatis-spring</artifactId>
 | 
			
		||||
            <version>1.3.2</version>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>org.springframework</groupId>
 | 
			
		||||
            <artifactId>spring-jdbc</artifactId>
 | 
			
		||||
            <version>5.0.7.RELEASE</version>
 | 
			
		||||
        </dependency>-->
 | 
			
		||||
        <!--引入mysql驱动-->
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>mysql</groupId>
 | 
			
		||||
            <artifactId>mysql-connector-java</artifactId>
 | 
			
		||||
            <version>6.0.6</version>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <!--druid 依赖-->
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>com.alibaba</groupId>
 | 
			
		||||
            <artifactId>druid-spring-boot-starter</artifactId>
 | 
			
		||||
            <version>1.1.10</version>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <!--aop starter-->
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>org.springframework.boot</groupId>
 | 
			
		||||
            <artifactId>spring-boot-starter-aop</artifactId>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <!--web starter -->
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>org.springframework.boot</groupId>
 | 
			
		||||
            <artifactId>spring-boot-starter-web</artifactId>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <!--分布式事务依赖-->
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>org.springframework.boot</groupId>
 | 
			
		||||
            <artifactId>spring-boot-starter-jta-atomikos</artifactId>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>org.projectlombok</groupId>
 | 
			
		||||
            <artifactId>lombok</artifactId>
 | 
			
		||||
            <optional>true</optional>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>org.springframework.boot</groupId>
 | 
			
		||||
            <artifactId>spring-boot-starter-test</artifactId>
 | 
			
		||||
            <scope>test</scope>
 | 
			
		||||
        </dependency>
 | 
			
		||||
    </dependencies>
 | 
			
		||||
 | 
			
		||||
    <build>
 | 
			
		||||
        <plugins>
 | 
			
		||||
            <plugin>
 | 
			
		||||
                <groupId>org.springframework.boot</groupId>
 | 
			
		||||
                <artifactId>spring-boot-maven-plugin</artifactId>
 | 
			
		||||
            </plugin>
 | 
			
		||||
        </plugins>
 | 
			
		||||
    </build>
 | 
			
		||||
 | 
			
		||||
</project>
 | 
			
		||||
@@ -0,0 +1,15 @@
 | 
			
		||||
package com.heibaiying.springboot;
 | 
			
		||||
 | 
			
		||||
import org.springframework.boot.SpringApplication;
 | 
			
		||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
 | 
			
		||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
 | 
			
		||||
 | 
			
		||||
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
 | 
			
		||||
public class DruidMybatisMultiApplication {
 | 
			
		||||
 | 
			
		||||
    public static void main(String[] args) {
 | 
			
		||||
        SpringApplication.run(DruidMybatisMultiApplication.class, args);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -0,0 +1,40 @@
 | 
			
		||||
package com.heibaiying.springboot.aop;
 | 
			
		||||
 | 
			
		||||
import com.heibaiying.springboot.config.DataSourceContextHolder;
 | 
			
		||||
import com.heibaiying.springboot.constant.Data;
 | 
			
		||||
import org.aspectj.lang.JoinPoint;
 | 
			
		||||
import org.aspectj.lang.annotation.After;
 | 
			
		||||
import org.aspectj.lang.annotation.Aspect;
 | 
			
		||||
import org.aspectj.lang.annotation.Before;
 | 
			
		||||
import org.aspectj.lang.annotation.Pointcut;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author : heibaiying
 | 
			
		||||
 * @description : 动态方式去切换数据源
 | 
			
		||||
 */
 | 
			
		||||
@Aspect
 | 
			
		||||
@Component
 | 
			
		||||
public class DynamicDataSourceAspect {
 | 
			
		||||
 | 
			
		||||
    @Pointcut(value = "execution(* com.heibaiying.springboot.dao.*.*(..))")
 | 
			
		||||
    public void dataSourcePointCut() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Before(value = "dataSourcePointCut()")
 | 
			
		||||
    public void beforeSwitchDS(JoinPoint point) {
 | 
			
		||||
 | 
			
		||||
        Object[] args = point.getArgs();
 | 
			
		||||
 | 
			
		||||
        if (args == null || args.length < 1 || !Data.DATASOURCE2.equals(args[0])) {
 | 
			
		||||
            DataSourceContextHolder.setDataKey(Data.DATASOURCE1);
 | 
			
		||||
        } else {
 | 
			
		||||
            DataSourceContextHolder.setDataKey(Data.DATASOURCE2);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @After(value = "dataSourcePointCut()")
 | 
			
		||||
    public void afterSwitchDS(JoinPoint point) {
 | 
			
		||||
        DataSourceContextHolder.clearDataKey();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,34 @@
 | 
			
		||||
package com.heibaiying.springboot.bean;
 | 
			
		||||
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
 | 
			
		||||
import java.util.Date;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author : heibaiying
 | 
			
		||||
 */
 | 
			
		||||
@Data
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
@NoArgsConstructor
 | 
			
		||||
public class Programmer {
 | 
			
		||||
 | 
			
		||||
    private int id;
 | 
			
		||||
 | 
			
		||||
    private String name;
 | 
			
		||||
 | 
			
		||||
    private int age;
 | 
			
		||||
 | 
			
		||||
    private float salary;
 | 
			
		||||
 | 
			
		||||
    private Date birthday;
 | 
			
		||||
 | 
			
		||||
    public Programmer(String name, int age, float salary, Date birthday) {
 | 
			
		||||
        this.name = name;
 | 
			
		||||
        this.age = age;
 | 
			
		||||
        this.salary = salary;
 | 
			
		||||
        this.birthday = birthday;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,47 @@
 | 
			
		||||
package com.heibaiying.springboot.config;
 | 
			
		||||
 | 
			
		||||
import com.atomikos.jdbc.AtomikosDataSourceBean;
 | 
			
		||||
import org.springframework.core.env.Environment;
 | 
			
		||||
 | 
			
		||||
import javax.sql.DataSource;
 | 
			
		||||
import java.util.Properties;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author : heibaiying
 | 
			
		||||
 * @description :
 | 
			
		||||
 */
 | 
			
		||||
public class AbstractDataSourceConfig {
 | 
			
		||||
 | 
			
		||||
    public 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.setUniqueResourceName(dataSourceName);
 | 
			
		||||
        ds.setXaProperties(prop);
 | 
			
		||||
        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("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));
 | 
			
		||||
        //prop.put("useGlobalDataSourceStat",env.getProperty(prefix + "useGlobalDataSourceStat", Boolean.class));
 | 
			
		||||
        //prop.put("filters", env.getProperty(prefix + "filters"));
 | 
			
		||||
        return prop;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,739 @@
 | 
			
		||||
package com.heibaiying.springboot.config;
 | 
			
		||||
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.apache.ibatis.exceptions.PersistenceException;
 | 
			
		||||
import org.apache.ibatis.executor.BatchResult;
 | 
			
		||||
import org.apache.ibatis.session.*;
 | 
			
		||||
import org.mybatis.spring.MyBatisExceptionTranslator;
 | 
			
		||||
import org.mybatis.spring.SqlSessionTemplate;
 | 
			
		||||
import org.springframework.dao.support.PersistenceExceptionTranslator;
 | 
			
		||||
import org.springframework.util.Assert;
 | 
			
		||||
 | 
			
		||||
import java.lang.reflect.InvocationHandler;
 | 
			
		||||
import java.lang.reflect.Method;
 | 
			
		||||
import java.sql.Connection;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
import static java.lang.reflect.Proxy.newProxyInstance;
 | 
			
		||||
import static org.apache.ibatis.reflection.ExceptionUtil.unwrapThrowable;
 | 
			
		||||
import static org.mybatis.spring.SqlSessionUtils.*;
 | 
			
		||||
 | 
			
		||||
/***
 | 
			
		||||
 * @description: 自定义 sqlSessionTemplate 实现
 | 
			
		||||
 * 这个方法继承自sqlSessionTemplate,主要是重写了getSqlSessionFactory方法,并在getSqlSession时候传入我们用此方法得到的SqlSessionFactory
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
@Slf4j
 | 
			
		||||
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> targetSqlSessionFactories;
 | 
			
		||||
    private SqlSessionFactory 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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setTargetSqlSessionFactories(Map<Object, SqlSessionFactory> targetSqlSessionFactories) {
 | 
			
		||||
        this.targetSqlSessionFactories = targetSqlSessionFactories;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setDefaultTargetSqlSessionFactory(SqlSessionFactory defaultTargetSqlSessionFactory) {
 | 
			
		||||
        this.defaultTargetSqlSessionFactory = defaultTargetSqlSessionFactory;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
/***
 | 
			
		||||
     *  获取当前使用数据源对应的会话工厂
 | 
			
		||||
     *//*
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public SqlSessionFactory getSqlSessionFactory() {
 | 
			
		||||
 | 
			
		||||
        String dataSourceKey = DataSourceContextHolder.getDataKey();
 | 
			
		||||
        log.info("SqlSessionFactory key : {}",dataSourceKey);
 | 
			
		||||
        SqlSessionFactory targetSqlSessionFactory = targetSqlSessionFactories.get(dataSourceKey);
 | 
			
		||||
        if (targetSqlSessionFactory != null) {
 | 
			
		||||
            return targetSqlSessionFactory;
 | 
			
		||||
        } else if (defaultTargetSqlSessionFactory != null) {
 | 
			
		||||
            return defaultTargetSqlSessionFactory;
 | 
			
		||||
        } else {
 | 
			
		||||
            Assert.notNull(targetSqlSessionFactories, "Property 'targetSqlSessionFactories' or 'defaultTargetSqlSessionFactory' are required");
 | 
			
		||||
            Assert.notNull(defaultTargetSqlSessionFactory, "Property 'defaultTargetSqlSessionFactory' or 'targetSqlSessionFactories' are required");
 | 
			
		||||
        }
 | 
			
		||||
        return this.sqlSessionFactory;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
/**
 | 
			
		||||
     * 这个方法的实现和父类的实现是基本一致的,唯一不同的就是在getSqlSession方法传参中获取会话工厂的方式
 | 
			
		||||
     *//*
 | 
			
		||||
 | 
			
		||||
    private class SqlSessionInterceptor implements InvocationHandler {
 | 
			
		||||
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 | 
			
		||||
            //在getSqlSession传参时候,用我们重写的getSqlSessionFactory获取当前数据源对应的会话工厂
 | 
			
		||||
            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())) {
 | 
			
		||||
                    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());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ExecutorType getExecutorType() {
 | 
			
		||||
        return this.executorType;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public PersistenceExceptionTranslator getPersistenceExceptionTranslator() {
 | 
			
		||||
        return this.exceptionTranslator;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
/**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     *//*
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public <T> T selectOne(String statement) {
 | 
			
		||||
        return this.sqlSessionProxy.<T> selectOne(statement);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
/**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     *//*
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public <T> T selectOne(String statement, Object parameter) {
 | 
			
		||||
        return this.sqlSessionProxy.<T> selectOne(statement, parameter);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
/**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     *//*
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public <K, V> Map<K, V> selectMap(String statement, String mapKey) {
 | 
			
		||||
        return this.sqlSessionProxy.<K, V> selectMap(statement, mapKey);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
/**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     *//*
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) {
 | 
			
		||||
        return this.sqlSessionProxy.<K, V> selectMap(statement, parameter, mapKey);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
/**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     *//*
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    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}
 | 
			
		||||
     *//*
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public <T> Cursor<T> selectCursor(String statement) {
 | 
			
		||||
        return this.sqlSessionProxy.selectCursor(statement);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
/**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     *//*
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public <T> Cursor<T> selectCursor(String statement, Object parameter) {
 | 
			
		||||
        return this.sqlSessionProxy.selectCursor(statement, parameter);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
/**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     *//*
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds) {
 | 
			
		||||
        return this.sqlSessionProxy.selectCursor(statement, parameter, rowBounds);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
/**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     *//*
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public <E> List<E> selectList(String statement) {
 | 
			
		||||
        return this.sqlSessionProxy.<E> selectList(statement);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
/**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     *//*
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public <E> List<E> selectList(String statement, Object parameter) {
 | 
			
		||||
        return this.sqlSessionProxy.<E> selectList(statement, parameter);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
/**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     *//*
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
 | 
			
		||||
        return this.sqlSessionProxy.<E> selectList(statement, parameter, rowBounds);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
/**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     *//*
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void select(String statement, ResultHandler handler) {
 | 
			
		||||
        this.sqlSessionProxy.select(statement, handler);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
/**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     *//*
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void select(String statement, Object parameter, ResultHandler handler) {
 | 
			
		||||
        this.sqlSessionProxy.select(statement, parameter, handler);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
/**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     *//*
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
 | 
			
		||||
        this.sqlSessionProxy.select(statement, parameter, rowBounds, handler);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
/**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     *//*
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int insert(String statement) {
 | 
			
		||||
        return this.sqlSessionProxy.insert(statement);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
/**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     *//*
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int insert(String statement, Object parameter) {
 | 
			
		||||
        return this.sqlSessionProxy.insert(statement, parameter);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
/**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     *//*
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int update(String statement) {
 | 
			
		||||
        return this.sqlSessionProxy.update(statement);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
/**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     *//*
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int update(String statement, Object parameter) {
 | 
			
		||||
        return this.sqlSessionProxy.update(statement, parameter);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
/**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     *//*
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int delete(String statement) {
 | 
			
		||||
        return this.sqlSessionProxy.delete(statement);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
/**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     *//*
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int delete(String statement, Object parameter) {
 | 
			
		||||
        return this.sqlSessionProxy.delete(statement, parameter);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
/**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     *//*
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public <T> T getMapper(Class<T> type) {
 | 
			
		||||
        return getConfiguration().getMapper(type, this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
/**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     *//*
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void commit() {
 | 
			
		||||
        throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
/**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     *//*
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void commit(boolean force) {
 | 
			
		||||
        throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
/**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     *//*
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void rollback() {
 | 
			
		||||
        throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
/**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     *//*
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void rollback(boolean force) {
 | 
			
		||||
        throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
/**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     *//*
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void close() {
 | 
			
		||||
        throw new UnsupportedOperationException("Manual close is not allowed over a Spring managed SqlSession");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
/**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     *//*
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void clearCache() {
 | 
			
		||||
        this.sqlSessionProxy.clearCache();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
/**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     *
 | 
			
		||||
     *//*
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Configuration getConfiguration() {
 | 
			
		||||
        return this.sqlSessionFactory.getConfiguration();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
/**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     *//*
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Connection getConnection() {
 | 
			
		||||
        return this.sqlSessionProxy.getConnection();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
/**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     *
 | 
			
		||||
     * @since 1.0.2
 | 
			
		||||
     *
 | 
			
		||||
     *//*
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public List<BatchResult> flushStatements() {
 | 
			
		||||
        return this.sqlSessionProxy.flushStatements();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void destroy() throws Exception {
 | 
			
		||||
        //This method forces spring disposer to avoid call of SqlSessionTemplate.close() which gives UnsupportedOperationException
 | 
			
		||||
    }
 | 
			
		||||
*/
 | 
			
		||||
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 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() {
 | 
			
		||||
 | 
			
		||||
        String dataSourceKey = DataSourceContextHolder.getDataKey();
 | 
			
		||||
        System.out.println("当前会话工厂 : "+dataSourceKey);
 | 
			
		||||
        SqlSessionFactory targetSqlSessionFactory = targetSqlSessionFactorys.get(dataSourceKey);
 | 
			
		||||
        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());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,145 @@
 | 
			
		||||
package com.heibaiying.springboot.config;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.druid.pool.xa.DruidXADataSource;
 | 
			
		||||
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
 | 
			
		||||
import com.heibaiying.springboot.constant.Data;
 | 
			
		||||
import org.apache.ibatis.logging.stdout.StdOutImpl;
 | 
			
		||||
import org.apache.ibatis.session.SqlSessionFactory;
 | 
			
		||||
import org.mybatis.spring.SqlSessionFactoryBean;
 | 
			
		||||
import org.mybatis.spring.annotation.MapperScan;
 | 
			
		||||
import org.springframework.boot.context.properties.ConfigurationProperties;
 | 
			
		||||
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.support.PathMatchingResourcePatternResolver;
 | 
			
		||||
 | 
			
		||||
import javax.sql.DataSource;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author : heibaiying
 | 
			
		||||
 * @description : 多数据源配置
 | 
			
		||||
 * 需要特别强调的是以下任意的DataSource的bean都不要用@Primary去修饰,包括动态数据源。原因是在事务下,spring在DataSourceUtils类中doGetConnection方法中判断获取当前数据源是否持有连接的时候,
 | 
			
		||||
 * 总是会传入用@Primary修饰的数据源,如果一个事务下有多个数据源,这种情况下就会出现connection复用,无法切换数据源, 源码截图详见README.md
 | 
			
		||||
 */
 | 
			
		||||
@Configuration
 | 
			
		||||
@MapperScan(basePackages = DataSourceConfig.BASE_PACKAGES, sqlSessionTemplateRef = "sqlSessionTemplate")
 | 
			
		||||
public class DataSourceConfig extends AbstractDataSourceConfig {
 | 
			
		||||
 | 
			
		||||
    static final String BASE_PACKAGES = "com.heibaiying.springboot.dao";
 | 
			
		||||
 | 
			
		||||
    private static final String MAPPER_LOCATION = "classpath:mappers/*.xml";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 创建数据源1,不要用@Primary去修饰
 | 
			
		||||
     */
 | 
			
		||||
    @Bean
 | 
			
		||||
    public DataSource dataSourceOne(Environment env) {
 | 
			
		||||
        String prefix = "spring.datasource.druid.db1.";
 | 
			
		||||
        return getDataSource(env, prefix, "one");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 创建数据源2,不要用@Primary去修饰
 | 
			
		||||
     */
 | 
			
		||||
    @Bean
 | 
			
		||||
    public DataSource dataSourceTwo(Environment env) {
 | 
			
		||||
        String prefix = "spring.datasource.druid.db2.";
 | 
			
		||||
        return getDataSource(env, prefix, "two");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  /* @Bean
 | 
			
		||||
   @ConfigurationProperties("spring.datasource.druid.db1")
 | 
			
		||||
   public DataSource dataSourceOne() {
 | 
			
		||||
       return new DruidXADataSource();
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   @Bean
 | 
			
		||||
   @ConfigurationProperties("spring.datasource.druid.db2")
 | 
			
		||||
   public DataSource dataSourceTwo() {
 | 
			
		||||
       return new DruidXADataSource();
 | 
			
		||||
   }*/
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 设置多数据源,不要用@Primary去修饰
 | 
			
		||||
     */
 | 
			
		||||
    @Bean
 | 
			
		||||
    public DataSource dynamicDataSource(DataSource dataSourceOne, DataSource dataSourceTwo) {
 | 
			
		||||
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
 | 
			
		||||
        // 设置默认数据源
 | 
			
		||||
        dynamicDataSource.setDefaultTargetDataSource(dataSourceOne);
 | 
			
		||||
        // 设置多数据源
 | 
			
		||||
        Map<Object, Object> dsMap = new HashMap<>();
 | 
			
		||||
        dsMap.put(Data.DATASOURCE1, dataSourceOne);
 | 
			
		||||
        dsMap.put(Data.DATASOURCE2, dataSourceTwo);
 | 
			
		||||
 | 
			
		||||
        dynamicDataSource.setTargetDataSources(dsMap);
 | 
			
		||||
        return dynamicDataSource;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param dataSourceOne 数据源1
 | 
			
		||||
     * @return 数据源1的会话工厂
 | 
			
		||||
     */
 | 
			
		||||
    @Bean
 | 
			
		||||
    public SqlSessionFactory sqlSessionFactoryOne(DataSource dataSourceOne)
 | 
			
		||||
            throws Exception {
 | 
			
		||||
        return createSqlSessionFactory(dataSourceOne);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param dataSourceTwo 数据源2
 | 
			
		||||
     * @return 数据源2的会话工厂
 | 
			
		||||
     */
 | 
			
		||||
    @Bean
 | 
			
		||||
    public SqlSessionFactory sqlSessionFactoryTwo(DataSource dataSourceTwo)
 | 
			
		||||
            throws Exception {
 | 
			
		||||
        return createSqlSessionFactory(dataSourceTwo);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /***
 | 
			
		||||
     * sqlSessionTemplate与Spring事务管理一起使用,以确保使用的实际SqlSession是与当前Spring事务关联的,
 | 
			
		||||
     * 此外它还管理会话生命周期,包括根据Spring事务配置根据需要关闭,提交或回滚会话
 | 
			
		||||
     * @param sqlSessionFactoryOne 数据源1
 | 
			
		||||
     * @param sqlSessionFactoryTwo 数据源2
 | 
			
		||||
     */
 | 
			
		||||
    @Bean
 | 
			
		||||
    public CustomSqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactoryOne,
 | 
			
		||||
                                                       SqlSessionFactory sqlSessionFactoryTwo) {
 | 
			
		||||
        Map<Object, SqlSessionFactory> sqlSessionFactoryMap = new HashMap<>();
 | 
			
		||||
        sqlSessionFactoryMap.put(Data.DATASOURCE1, sqlSessionFactoryOne);
 | 
			
		||||
        sqlSessionFactoryMap.put(Data.DATASOURCE2, sqlSessionFactoryTwo);
 | 
			
		||||
 | 
			
		||||
        CustomSqlSessionTemplate customSqlSessionTemplate = new CustomSqlSessionTemplate(sqlSessionFactoryOne);
 | 
			
		||||
        customSqlSessionTemplate.setTargetSqlSessionFactorys(sqlSessionFactoryMap);
 | 
			
		||||
        return customSqlSessionTemplate;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /***
 | 
			
		||||
     * 自定义会话工厂
 | 
			
		||||
     * @param dataSource 数据源
 | 
			
		||||
     * @return :自定义的会话工厂
 | 
			
		||||
     */
 | 
			
		||||
    private SqlSessionFactory createSqlSessionFactory(DataSource dataSource) throws Exception {
 | 
			
		||||
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
 | 
			
		||||
        factoryBean.setDataSource(dataSource);
 | 
			
		||||
        factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_LOCATION));
 | 
			
		||||
        // 其他可配置项(包括是否打印sql,是否开启驼峰命名等)
 | 
			
		||||
        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
 | 
			
		||||
        configuration.setMapUnderscoreToCamelCase(true);
 | 
			
		||||
        configuration.setLogImpl(StdOutImpl.class);
 | 
			
		||||
        factoryBean.setConfiguration(configuration);
 | 
			
		||||
        /* *
 | 
			
		||||
         * 采用个如下方式配置属性的时候一定要保证已经进行数据源的配置(setDataSource)和数据源和MapperLocation配置(setMapperLocations)
 | 
			
		||||
         * factoryBean.getObject().getConfiguration().setMapUnderscoreToCamelCase(true);
 | 
			
		||||
         * factoryBean.getObject().getConfiguration().setLogImpl(StdOutImpl.class);
 | 
			
		||||
         **/
 | 
			
		||||
        return factoryBean.getObject();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,25 @@
 | 
			
		||||
package com.heibaiying.springboot.config;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author : heibaiying
 | 
			
		||||
 * @description : 保证数据源切换的线程隔离
 | 
			
		||||
 */
 | 
			
		||||
public class DataSourceContextHolder {
 | 
			
		||||
 | 
			
		||||
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
 | 
			
		||||
 | 
			
		||||
    // 设置数据源名
 | 
			
		||||
    public static void setDataKey(String dbName) {
 | 
			
		||||
        contextHolder.set(dbName);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 获取数据源名
 | 
			
		||||
    public static String getDataKey() {
 | 
			
		||||
        return (contextHolder.get());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 清除数据源名
 | 
			
		||||
    public static void clearDataKey() {
 | 
			
		||||
        contextHolder.remove();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,16 @@
 | 
			
		||||
package com.heibaiying.springboot.config;
 | 
			
		||||
 | 
			
		||||
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author : heibaiying
 | 
			
		||||
 * @description : 动态数据源配置
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
public class DynamicDataSource extends AbstractRoutingDataSource {
 | 
			
		||||
    @Override
 | 
			
		||||
    protected Object determineCurrentLookupKey() {
 | 
			
		||||
        System.out.println("当前数据库:"+DataSourceContextHolder.getDataKey());
 | 
			
		||||
        return DataSourceContextHolder.getDataKey();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,42 @@
 | 
			
		||||
package com.heibaiying.springboot.config;
 | 
			
		||||
 | 
			
		||||
import com.atomikos.icatch.jta.UserTransactionImp;
 | 
			
		||||
import com.atomikos.icatch.jta.UserTransactionManager;
 | 
			
		||||
import org.springframework.context.annotation.Bean;
 | 
			
		||||
import org.springframework.context.annotation.Configuration;
 | 
			
		||||
import org.springframework.transaction.PlatformTransactionManager;
 | 
			
		||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
 | 
			
		||||
import org.springframework.transaction.jta.JtaTransactionManager;
 | 
			
		||||
 | 
			
		||||
import javax.transaction.TransactionManager;
 | 
			
		||||
import javax.transaction.UserTransaction;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author : heibaiying
 | 
			
		||||
 * @description : 分布式事务配置
 | 
			
		||||
 */
 | 
			
		||||
@Configuration
 | 
			
		||||
@EnableTransactionManagement
 | 
			
		||||
public class XATransactionManagerConfig {
 | 
			
		||||
 | 
			
		||||
    @Bean
 | 
			
		||||
    public UserTransaction userTransaction() throws Throwable {
 | 
			
		||||
        UserTransactionImp userTransactionImp = new UserTransactionImp();
 | 
			
		||||
        userTransactionImp.setTransactionTimeout(10000);
 | 
			
		||||
        return userTransactionImp;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Bean(initMethod = "init", destroyMethod = "close")
 | 
			
		||||
    public TransactionManager atomikosTransactionManager() {
 | 
			
		||||
        UserTransactionManager userTransactionManager = new UserTransactionManager();
 | 
			
		||||
        userTransactionManager.setForceShutdown(false);
 | 
			
		||||
        return userTransactionManager;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Bean
 | 
			
		||||
    public PlatformTransactionManager transactionManager(UserTransaction userTransaction,
 | 
			
		||||
                                                         TransactionManager transactionManager) {
 | 
			
		||||
        return new JtaTransactionManager(userTransaction, transactionManager);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -0,0 +1,11 @@
 | 
			
		||||
package com.heibaiying.springboot.constant;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author : heibaiying
 | 
			
		||||
 * @description :
 | 
			
		||||
 */
 | 
			
		||||
public class Data {
 | 
			
		||||
 | 
			
		||||
    public static final String DATASOURCE1="DATASOURCE1";
 | 
			
		||||
    public static final String DATASOURCE2="DATASOURCE2";
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,61 @@
 | 
			
		||||
package com.heibaiying.springboot.controller;
 | 
			
		||||
 | 
			
		||||
import com.heibaiying.springboot.bean.Programmer;
 | 
			
		||||
import com.heibaiying.springboot.constant.Data;
 | 
			
		||||
import com.heibaiying.springboot.dao.ProgrammerMapper;
 | 
			
		||||
import org.springframework.beans.factory.annotation.Autowired;
 | 
			
		||||
import org.springframework.transaction.annotation.Transactional;
 | 
			
		||||
import org.springframework.web.bind.annotation.GetMapping;
 | 
			
		||||
import org.springframework.web.bind.annotation.RestController;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author : heibaiying
 | 
			
		||||
 * @description : 测试controller
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
@RestController
 | 
			
		||||
public class TestController {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @Autowired
 | 
			
		||||
    private ProgrammerMapper programmerDao;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 查询全部数据源的数据
 | 
			
		||||
     */
 | 
			
		||||
    @GetMapping("/db/programmers")
 | 
			
		||||
    public List<Programmer> getAllProgrammers() {
 | 
			
		||||
        List<Programmer> programmers = programmerDao.selectAll(Data.DATASOURCE1);
 | 
			
		||||
        programmers.addAll(programmerDao.selectAll(Data.DATASOURCE2));
 | 
			
		||||
        return programmers;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @GetMapping("ts/db/programmers")
 | 
			
		||||
    @Transactional
 | 
			
		||||
    public List<Programmer> getAllProgrammersTs() {
 | 
			
		||||
        List<Programmer> programmers = programmerDao.selectAll(Data.DATASOURCE1);
 | 
			
		||||
        programmers.addAll(programmerDao.selectAll(Data.DATASOURCE2));
 | 
			
		||||
        return programmers;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 不指定就使用默认的数据源
 | 
			
		||||
     */
 | 
			
		||||
    @GetMapping("/db1/programmers")
 | 
			
		||||
    public List<Programmer> getDB1Programmers() {
 | 
			
		||||
 | 
			
		||||
        return programmerDao.selectAll(null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 从指定数据源查询
 | 
			
		||||
     */
 | 
			
		||||
    @GetMapping("/db2/programmers")
 | 
			
		||||
    public List<Programmer> getDB2Programmers() {
 | 
			
		||||
        return programmerDao.selectAll(Data.DATASOURCE2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,40 @@
 | 
			
		||||
package com.heibaiying.springboot.controller;
 | 
			
		||||
 | 
			
		||||
import com.heibaiying.springboot.bean.Programmer;
 | 
			
		||||
import com.heibaiying.springboot.constant.Data;
 | 
			
		||||
import com.heibaiying.springboot.dao.ProgrammerMapper;
 | 
			
		||||
import org.springframework.beans.factory.annotation.Autowired;
 | 
			
		||||
import org.springframework.transaction.annotation.Transactional;
 | 
			
		||||
import org.springframework.web.bind.annotation.RequestMapping;
 | 
			
		||||
import org.springframework.web.bind.annotation.RestController;
 | 
			
		||||
 | 
			
		||||
import java.util.Date;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author : heibaiying
 | 
			
		||||
 * @description : 测试单数据库事务
 | 
			
		||||
 */
 | 
			
		||||
@RestController
 | 
			
		||||
public class TransactionController {
 | 
			
		||||
 | 
			
		||||
    @Autowired
 | 
			
		||||
    private ProgrammerMapper programmerDao;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @RequestMapping("db1/change")
 | 
			
		||||
    @Transactional
 | 
			
		||||
    public void changeDb1() {
 | 
			
		||||
        Programmer programmer = new Programmer(1, "xiaolandb1", 99, 6662.32f, new Date());
 | 
			
		||||
        programmerDao.modify(Data.DATASOURCE1, programmer);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @RequestMapping("ts/db1/change")
 | 
			
		||||
    @Transactional
 | 
			
		||||
    public void changeTsDb1() {
 | 
			
		||||
        Programmer programmer = new Programmer(1, "xiaolandb1", 88, 6662.32f, new Date());
 | 
			
		||||
        programmerDao.modify(Data.DATASOURCE1, programmer);
 | 
			
		||||
        // 抛出异常 查看回滚
 | 
			
		||||
        int j = 1 / 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,44 @@
 | 
			
		||||
package com.heibaiying.springboot.controller;
 | 
			
		||||
 | 
			
		||||
import com.heibaiying.springboot.bean.Programmer;
 | 
			
		||||
import com.heibaiying.springboot.constant.Data;
 | 
			
		||||
import com.heibaiying.springboot.dao.ProgrammerMapper;
 | 
			
		||||
import org.springframework.beans.factory.annotation.Autowired;
 | 
			
		||||
import org.springframework.transaction.annotation.Transactional;
 | 
			
		||||
import org.springframework.web.bind.annotation.RequestMapping;
 | 
			
		||||
import org.springframework.web.bind.annotation.RestController;
 | 
			
		||||
 | 
			
		||||
import java.util.Date;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author : heibaiying
 | 
			
		||||
 * @description : 测试分布式事务
 | 
			
		||||
 */
 | 
			
		||||
@RestController
 | 
			
		||||
public class XATransactionController {
 | 
			
		||||
 | 
			
		||||
    @Autowired
 | 
			
		||||
    private ProgrammerMapper programmerDao;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @RequestMapping("/db/change")
 | 
			
		||||
    @Transactional
 | 
			
		||||
    public void changeDb() {
 | 
			
		||||
        Programmer programmer01 = new Programmer(1, "xiaolandb1", 100, 6662.32f, new Date());
 | 
			
		||||
        Programmer programmer02 = new Programmer(1, "xiaohongdb2", 100, 6662.32f, new Date());
 | 
			
		||||
        programmerDao.modify(Data.DATASOURCE1, programmer01);
 | 
			
		||||
        programmerDao.modify(Data.DATASOURCE2, programmer02);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @RequestMapping("ts/db/change")
 | 
			
		||||
    @Transactional
 | 
			
		||||
    public void changeTsDb() {
 | 
			
		||||
        Programmer programmer01 = new Programmer(1, "xiaolandb1", 99, 6662.32f, new Date());
 | 
			
		||||
        Programmer programmer02 = new Programmer(1, "xiaohongdb2", 99, 6662.32f, new Date());
 | 
			
		||||
        programmerDao.modify(Data.DATASOURCE1, programmer01);
 | 
			
		||||
        programmerDao.modify(Data.DATASOURCE2, programmer02);
 | 
			
		||||
        int i = 1 / 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,24 @@
 | 
			
		||||
package com.heibaiying.springboot.dao;
 | 
			
		||||
 | 
			
		||||
import com.heibaiying.springboot.bean.Programmer;
 | 
			
		||||
import org.apache.ibatis.annotations.Mapper;
 | 
			
		||||
import org.apache.ibatis.annotations.Param;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author : heibaiying
 | 
			
		||||
 */
 | 
			
		||||
@Mapper
 | 
			
		||||
public interface ProgrammerMapper {
 | 
			
		||||
 | 
			
		||||
    List<Programmer> selectAll(String dataSource);
 | 
			
		||||
 | 
			
		||||
    void save(Programmer programmer);
 | 
			
		||||
 | 
			
		||||
    Programmer selectById(int id);
 | 
			
		||||
 | 
			
		||||
    int modify(String dataSource,@Param("pro") Programmer programmer);
 | 
			
		||||
 | 
			
		||||
    void delete(int id);
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,113 @@
 | 
			
		||||
spring:
 | 
			
		||||
  datasource:
 | 
			
		||||
    druid:
 | 
			
		||||
      db1:
 | 
			
		||||
        url: jdbc:mysql://127.0.0.1:3306/mysql?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false
 | 
			
		||||
        username: root
 | 
			
		||||
        password: root
 | 
			
		||||
        driver-class-name: com.mysql.cj.jdbc.Driver
 | 
			
		||||
        # 使用 druid 作为连接池
 | 
			
		||||
        type: com.alibaba.druid.pool.DruidDataSource
 | 
			
		||||
 | 
			
		||||
        # 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
 | 
			
		||||
        initialSize: 5
 | 
			
		||||
        # 最小连接池数量
 | 
			
		||||
        minIdle: 5
 | 
			
		||||
        # 最大连接池数量
 | 
			
		||||
        maxActive: 10
 | 
			
		||||
        # 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
 | 
			
		||||
        maxWait: 60000
 | 
			
		||||
        # Destroy线程会检测连接的间隔时间,如果连接空闲时间大于等于minEvictableIdleTimeMillis则关闭物理连接。
 | 
			
		||||
        timeBetweenEvictionRunsMillis: 60000
 | 
			
		||||
        # 连接保持空闲而不被驱逐的最小时间
 | 
			
		||||
        minEvictableIdleTimeMillis: 300000
 | 
			
		||||
        # 用来检测连接是否有效的sql 因数据库方言而异, 例如 oracle 应该写成 SELECT 1 FROM DUAL
 | 
			
		||||
        validationQuery: SELECT 9
 | 
			
		||||
        # 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
 | 
			
		||||
        testWhileIdle: true
 | 
			
		||||
        # 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
 | 
			
		||||
        testOnBorrow: false
 | 
			
		||||
        # 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
 | 
			
		||||
        testOnReturn: false
 | 
			
		||||
        # 是否自动回收超时连接
 | 
			
		||||
        removeAbandoned: true
 | 
			
		||||
        # 超时时间(以秒数为单位)
 | 
			
		||||
        remove-abandoned-timeout: 180
 | 
			
		||||
        # WebStatFilter用于采集web-jdbc关联监控的数据。
 | 
			
		||||
        web-stat-filter:
 | 
			
		||||
          # 是否开启 WebStatFilter 默认是true
 | 
			
		||||
          enabled: true
 | 
			
		||||
          # 需要拦截的url
 | 
			
		||||
          url-pattern: /*
 | 
			
		||||
          # 排除静态资源的请求
 | 
			
		||||
          exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"
 | 
			
		||||
 | 
			
		||||
        # Druid内置提供了一个StatViewServlet用于展示Druid的统计信息。
 | 
			
		||||
        stat-view-servlet:
 | 
			
		||||
          #是否启用StatViewServlet 默认值true
 | 
			
		||||
          enabled: true
 | 
			
		||||
          # 需要拦截的url
 | 
			
		||||
          url-pattern: /druid/*
 | 
			
		||||
          # 允许清空统计数据
 | 
			
		||||
          reset-enable: true
 | 
			
		||||
          login-username: druid
 | 
			
		||||
          login-password: druid
 | 
			
		||||
      db2:
 | 
			
		||||
        url: jdbc:mysql://127.0.0.1:3306/mysql02?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false
 | 
			
		||||
        username: root
 | 
			
		||||
        password: root
 | 
			
		||||
        driver-class-name: com.mysql.cj.jdbc.Driver
 | 
			
		||||
        # 使用 druid 作为连接池
 | 
			
		||||
        type: com.alibaba.druid.pool.DruidDataSource
 | 
			
		||||
 | 
			
		||||
        # 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
 | 
			
		||||
        initialSize: 5
 | 
			
		||||
        # 最小连接池数量
 | 
			
		||||
        minIdle: 5
 | 
			
		||||
        # 最大连接池数量
 | 
			
		||||
        maxActive: 10
 | 
			
		||||
        # 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
 | 
			
		||||
        maxWait: 60000
 | 
			
		||||
        # Destroy线程会检测连接的间隔时间,如果连接空闲时间大于等于minEvictableIdleTimeMillis则关闭物理连接。
 | 
			
		||||
        timeBetweenEvictionRunsMillis: 60000
 | 
			
		||||
        # 连接保持空闲而不被驱逐的最小时间
 | 
			
		||||
        minEvictableIdleTimeMillis: 300000
 | 
			
		||||
        # 用来检测连接是否有效的sql 因数据库方言而异, 例如 oracle 应该写成 SELECT 1 FROM DUAL
 | 
			
		||||
        validationQuery: SELECT 9
 | 
			
		||||
        # 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
 | 
			
		||||
        testWhileIdle: true
 | 
			
		||||
        # 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
 | 
			
		||||
        testOnBorrow: false
 | 
			
		||||
        # 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
 | 
			
		||||
        testOnReturn: false
 | 
			
		||||
        # 是否自动回收超时连接
 | 
			
		||||
        removeAbandoned: true
 | 
			
		||||
        # 超时时间(以秒数为单位)
 | 
			
		||||
        remove-abandoned-timeout: 180
 | 
			
		||||
        # WebStatFilter用于采集web-jdbc关联监控的数据。
 | 
			
		||||
        web-stat-filter:
 | 
			
		||||
          # 是否开启 WebStatFilter 默认是true
 | 
			
		||||
          enabled: true
 | 
			
		||||
          # 需要拦截的url
 | 
			
		||||
          url-pattern: /*
 | 
			
		||||
          # 排除静态资源的请求
 | 
			
		||||
          exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"
 | 
			
		||||
 | 
			
		||||
        # Druid内置提供了一个StatViewServlet用于展示Druid的统计信息。
 | 
			
		||||
        stat-view-servlet:
 | 
			
		||||
          #是否启用StatViewServlet 默认值true
 | 
			
		||||
          enabled: true
 | 
			
		||||
          # 需要拦截的url
 | 
			
		||||
          url-pattern: /druid/*
 | 
			
		||||
          # 允许清空统计数据
 | 
			
		||||
          reset-enable: true
 | 
			
		||||
          login-username: druid
 | 
			
		||||
          login-password: druid
 | 
			
		||||
# mybatis 相关配置
 | 
			
		||||
mybatis:
 | 
			
		||||
  configuration:
 | 
			
		||||
    # 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。
 | 
			
		||||
    # oracle数据库建议配置为JdbcType.NULL, 默认是Other
 | 
			
		||||
    jdbc-type-for-null: 'null'
 | 
			
		||||
    # 是否打印sql语句 调试的时候可以开启
 | 
			
		||||
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
 | 
			
		||||
@@ -0,0 +1,27 @@
 | 
			
		||||
<?xml version="1.0"  encoding="utf-8"?>
 | 
			
		||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 | 
			
		||||
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
 | 
			
		||||
 | 
			
		||||
<mapper namespace="com.heibaiying.springboot.dao.ProgrammerMapper">
 | 
			
		||||
 | 
			
		||||
    <select id="selectAll" resultType="com.heibaiying.springboot.bean.Programmer">
 | 
			
		||||
        select * from programmer
 | 
			
		||||
    </select>
 | 
			
		||||
 | 
			
		||||
    <insert id="save">
 | 
			
		||||
        insert into programmer (name, age, salary, birthday) VALUES (#{name}, #{age}, #{salary}, #{birthday})
 | 
			
		||||
    </insert>
 | 
			
		||||
 | 
			
		||||
    <select id="selectById" resultType="com.heibaiying.springboot.bean.Programmer">
 | 
			
		||||
      select * from programmer where name = #{id}
 | 
			
		||||
    </select>
 | 
			
		||||
 | 
			
		||||
    <update id="modify">
 | 
			
		||||
        update programmer set name=#{pro.name},age=#{pro.age},salary=#{pro.salary},birthday=#{pro.birthday} where id=#{pro.id}
 | 
			
		||||
    </update>
 | 
			
		||||
 | 
			
		||||
    <delete id="delete">
 | 
			
		||||
        delete from programmer where id = #{id}
 | 
			
		||||
    </delete>
 | 
			
		||||
 | 
			
		||||
</mapper>
 | 
			
		||||
@@ -0,0 +1,17 @@
 | 
			
		||||
package com.heibaiying.springboot;
 | 
			
		||||
 | 
			
		||||
import org.junit.Test;
 | 
			
		||||
import org.junit.runner.RunWith;
 | 
			
		||||
import org.springframework.boot.test.context.SpringBootTest;
 | 
			
		||||
import org.springframework.test.context.junit4.SpringRunner;
 | 
			
		||||
 | 
			
		||||
@RunWith(SpringRunner.class)
 | 
			
		||||
@SpringBootTest
 | 
			
		||||
public class SpringbootDruidMybatisMultiApplicationTests {
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void contextLoads() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								spring-boot/springboot-druid-mybatis-multi/tmlog101.log
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,9 @@
 | 
			
		||||
{"id":"10.0.75.1.tm154840078074100001","wasCommitted":true,"participants":[{"uri":"10.0.75.1.tm1","state":"COMMITTING","expires":1548400791454,"resourceName":"one"},{"uri":"10.0.75.1.tm2","state":"COMMITTING","expires":1548400791454,"resourceName":"two"}]}
 | 
			
		||||
{"id":"10.0.75.1.tm154840078074100001","wasCommitted":true,"participants":[{"uri":"10.0.75.1.tm1","state":"TERMINATED","expires":1548400791469,"resourceName":"one"},{"uri":"10.0.75.1.tm2","state":"TERMINATED","expires":1548400791469,"resourceName":"two"}]}
 | 
			
		||||
{"id":"10.0.75.1.tm154840078467900002","wasCommitted":true,"participants":[{"uri":"10.0.75.1.tm3","state":"COMMITTING","expires":1548400794622,"resourceName":"one"},{"uri":"10.0.75.1.tm4","state":"COMMITTING","expires":1548400794622,"resourceName":"two"}]}
 | 
			
		||||
{"id":"10.0.75.1.tm154840078467900002","wasCommitted":true,"participants":[{"uri":"10.0.75.1.tm3","state":"TERMINATED","expires":1548400794642,"resourceName":"one"},{"uri":"10.0.75.1.tm4","state":"TERMINATED","expires":1548400794642,"resourceName":"two"}]}
 | 
			
		||||
{"id":"10.0.75.1.tm154840078978400003","wasCommitted":false,"participants":[{"uri":"10.0.75.1.tm5","state":"TERMINATED","expires":1548400799772,"resourceName":"one"},{"uri":"10.0.75.1.tm6","state":"TERMINATED","expires":1548400799772,"resourceName":"two"}]}
 | 
			
		||||
{"id":"10.0.75.1.tm154840079364000004","wasCommitted":true,"participants":[{"uri":"10.0.75.1.tm7","state":"COMMITTING","expires":1548400803592,"resourceName":"one"},{"uri":"10.0.75.1.tm8","state":"COMMITTING","expires":1548400803592,"resourceName":"two"}]}
 | 
			
		||||
{"id":"10.0.75.1.tm154840079364000004","wasCommitted":true,"participants":[{"uri":"10.0.75.1.tm7","state":"TERMINATED","expires":1548400803609,"resourceName":"one"},{"uri":"10.0.75.1.tm8","state":"TERMINATED","expires":1548400803609,"resourceName":"two"}]}
 | 
			
		||||
{"id":"10.0.75.1.tm154840079686400005","wasCommitted":true,"participants":[{"uri":"10.0.75.1.tm9","state":"COMMITTING","expires":1548400806814,"resourceName":"one"},{"uri":"10.0.75.1.tm10","state":"COMMITTING","expires":1548400806814,"resourceName":"two"}]}
 | 
			
		||||
{"id":"10.0.75.1.tm154840079686400005","wasCommitted":true,"participants":[{"uri":"10.0.75.1.tm9","state":"TERMINATED","expires":1548400806831,"resourceName":"one"},{"uri":"10.0.75.1.tm10","state":"TERMINATED","expires":1548400806831,"resourceName":"two"}]}
 | 
			
		||||