Spring+Mybatis+Atomikos多數據源事務一致性

1、背景

由於應用多數據源的寫操作,一定會涉及到數據的一致性問題,由於mybatis-plus 的侷限性,採用spring+mybatis+atomikos+druid實現分佈式事務一致性。

2、實現

pom.xml

<?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>

    <groupId>com.tonytaotao</groupId>
    <artifactId>dtsc-job</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>dtsc-job</name>

    <properties>

        <!-- 文件拷貝時的編碼 -->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

        <!-- 編譯時的編碼 -->
        <maven.compiler.encoding>UTF-8</maven.compiler.encoding>

        <java.version>1.8</java.version>

        <maven-compiler-plugin.version>3.8.0</maven-compiler-plugin.version>
        <maven-resources-plugin.version>3.1.0</maven-resources-plugin.version>
        <maven-jar-plugin.version>3.1.0</maven-jar-plugin.version>

        <!-- quartz -->
        <quartz.version>2.3.0</quartz.version>

        <!-- spring -->
        <spring.version>5.1.2.RELEASE</spring.version>
        <aspectjweaver.version>1.9.2</aspectjweaver.version>

        <!-- distribute transaction -->
        <jta.version>1.1</jta.version>
        <transactions-jdbc.version>4.0.6</transactions-jdbc.version>


        <!-- datasource -->
        <spring-redis.version>2.1.2.RELEASE</spring-redis.version>
        <mysql.version>6.0.6</mysql.version>
        <druid.version>1.1.12</druid.version>
        <mybatis-plus.version>3.0.5</mybatis-plus.version>

        <!-- test -->
        <junit.version>4.12</junit.version>

        <!-- log4j2 -->
        <log4j2.version>2.11.1</log4j2.version>
        <slf4j.version>1.7.25</slf4j.version>
        <disruptor.version>3.4.2</disruptor.version>

        <!-- utils -->
        <lombok.version>1.18.4</lombok.version>
        <fastjson.version>1.2.51</fastjson.version>
        <joda-time.version>2.10.1</joda-time.version>
        <guava.version>27.0-jre</guava.version>
        <commons-lang3.version>3.8.1</commons-lang3.version>
        <velocity-engine-core.version>2.0</velocity-engine-core.version>

    </properties>

    <dependencies>

        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <version>${quartz.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>${aspectjweaver.version}</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>${aspectjweaver.version}</version>
        </dependency>

        <dependency>
            <groupId>javax.transaction</groupId>
            <artifactId>jta</artifactId>
            <version>${jta.version}</version>
        </dependency>
        <dependency>
            <groupId>com.atomikos</groupId>
            <artifactId>transactions-jdbc</artifactId>
            <version>${transactions-jdbc.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-redis</artifactId>
            <version>${spring-redis.version}</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>${druid.version}</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus</artifactId>
            <version>${mybatis-plus.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>${log4j2.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>${log4j2.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>${log4j2.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>com.lmax</groupId>
            <artifactId>disruptor</artifactId>
            <version>${disruptor.version}</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson.version}</version>
        </dependency>
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
            <version>${joda-time.version}</version>
        </dependency>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>${guava.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>${commons-lang3.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>${velocity-engine-core.version}</version>
        </dependency>

    </dependencies>

    <build>

        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/test/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>

        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${maven-compiler-plugin.version}</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <testSource>${java.version}</testSource>
                    <testTarget>${java.version}</testTarget>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <version>${maven-resources-plugin.version}</version>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>${maven-jar-plugin.version}</version>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>test</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>
                                target/classes/lib
                            </outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

2.1 JtaDatasourceConfig

package com.tonytaotao.dtsc.common.configuration.datasouce.jta; 

import com.alibaba.druid.pool.xa.DruidXADataSource; 
import com.atomikos.jdbc.AtomikosDataSourceBean; 
import com.tonytaotao.dtsc.common.configuration.datasouce.DBTypeEnum; 
import com.tonytaotao.dtsc.common.configuration.datasouce.DynamicDataSource; 
import org.springframework.beans.factory.annotation.Qualifier; 
import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.Configuration; 
import org.springframework.context.annotation.Primary; 

import javax.sql.DataSource; 
import java.util.HashMap; 
import java.util.Map; 

@Configuration 
public class JtaDatasourceConfig { 

@Bean("xaMultipleDataSource") 
@Primary 
public DynamicDataSource xaMultipleDataSource(@Qualifier("one") DataSource one, @Qualifier("two") DataSource two) { 
DynamicDataSource dynamicDataSource = new DynamicDataSource(); 
Map< Object, Object > targetDataSources = new HashMap<>(); 
targetDataSources.put(DBTypeEnum.ONE.getCode(), one); 
targetDataSources.put(DBTypeEnum.TWO.getCode(), two); 
dynamicDataSource.setTargetDataSources(targetDataSources); 
dynamicDataSource.setDefaultTargetDataSource(one); 
return dynamicDataSource; 


@Bean("one") 
public DataSource createXADatasourOne() { 

DruidXADataSource druidXADataSource = createDataSourceBase(); 
druidXADataSource.setUrl("jdbc:mysql://localhost:3306/dtsc?useSSL=false&useUnicode=true&allowPublicKeyRetrieval=true&characterEncoding=UTF8&serverTimezone=GMT"); 
druidXADataSource.setUsername("root"); 
druidXADataSource.setPassword("123456"); 

AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean(); 
atomikosDataSourceBean.setXaDataSourceClassName("com.alibaba.druid.pool.xa.DruidXADataSource"); 
atomikosDataSourceBean.setUniqueResourceName("one"); 
atomikosDataSourceBean.setXaDataSource(druidXADataSource); 
atomikosDataSourceBean.setMaintenanceInterval(30000); 
atomikosDataSourceBean.setTestQuery("SELECT 1"); 

return atomikosDataSourceBean; 


@Bean("two") 
public DataSource createXADatasourTwo() { 

DruidXADataSource druidXADataSource = createDataSourceBase(); 
druidXADataSource.setUrl("jdbc:mysql://localhost:3306/dtsc_1?useSSL=false&useUnicode=true&allowPublicKeyRetrieval=true&characterEncoding=UTF8&serverTimezone=GMT"); 
druidXADataSource.setUsername("root"); 
druidXADataSource.setPassword("123456"); 

AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean(); 
atomikosDataSourceBean.setXaDataSourceClassName("com.alibaba.druid.pool.xa.DruidXADataSource"); 
atomikosDataSourceBean.setUniqueResourceName("two"); 
atomikosDataSourceBean.setXaDataSource(druidXADataSource); 
atomikosDataSourceBean.setMaintenanceInterval(30000); 
atomikosDataSourceBean.setTestQuery("SELECT 1"); 

return atomikosDataSourceBean; 



private DruidXADataSource createDataSourceBase() { 

DruidXADataSource druidDataSource = new DruidXADataSource(); 

druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); 
druidDataSource.setInitialSize(50); 
druidDataSource.setMaxActive(100); 
druidDataSource.setMinIdle(5); 
druidDataSource.setMaxWait(1000); 
druidDataSource.setPoolPreparedStatements(false); 
druidDataSource.setMaxPoolPreparedStatementPerConnectionSize(-1); 
druidDataSource.setValidationQuery("SELECT 1"); 
druidDataSource.setValidationQueryTimeout(1); 
druidDataSource.setTestOnBorrow(false); 
druidDataSource.setTestOnReturn(false); 
druidDataSource.setTestWhileIdle(true); 
druidDataSource.setTimeBetweenEvictionRunsMillis(60000); 
druidDataSource.setMinEvictableIdleTimeMillis(30000); 

return druidDataSource; 


2.2 JtaTransationConfig

package com.tonytaotao.dtsc.common.configuration.datasouce.jta; 

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.context.annotation.EnableAspectJAutoProxy; 
import org.springframework.transaction.annotation.EnableTransactionManagement; 
import org.springframework.transaction.jta.JtaTransactionManager; 

import javax.transaction.SystemException; 

/** 
* @author wujintao 

* Configuration 類似於spring 配置文件,負責註冊bean 
* ComponentScan 註解類查找規則定義 <context:component-scan/> 
* EnableAspectJAutoProxy 激活Aspect 自動代理 <aop:aspectj-autoproxy/> 
* proxy-target-class : 默認爲false 表示使用JDK 動態代理。如果實現了至少一個接口,Spring 會優先選擇使用JDK 動態代理,若果沒有實現任何接口,則spring 會選擇CGLIB 動態代理,強制使用CGLIB 動態代理,使用以下配置 
* exposeProxy : springAOP 只會攔截public 方法,不會攔截provided 和private 方法,並且不會攔截public 方法內部調用的其他方法,也就是說只會攔截代理對象的方法,即增強的是代理對象,而不是原對象, 設置就可以暴露出代理對象,攔截器會獲取代理對象,並且將代理對象轉換成原對象。從而對內部調用的方法進行增強 
* EnableTransactionManagement 啓用註解式事務管理 <tx:annotation-driven /> 
*/ 
@Configuration 
@EnableTransactionManagement 
@EnableAspectJAutoProxy(exposeProxy = true) 
public class JtaTransationConfig { 

@Bean("jtaTransactionManager") 
public JtaTransactionManager jtaTransactionManager() { 

UserTransactionManager userTransactionManager = new UserTransactionManager(); 
userTransactionManager.setForceShutdown(true); 

UserTransactionImp userTransactionImp = new UserTransactionImp(); 
try { 
userTransactionImp.setTransactionTimeout(90000); 
} catch (SystemException e) { 
e.printStackTrace(); 


JtaTransactionManager jtaTransactionManager = new JtaTransactionManager(); 
jtaTransactionManager.setTransactionManager(userTransactionManager); 
jtaTransactionManager.setUserTransaction(userTransactionImp); 
jtaTransactionManager.setAllowCustomIsolationLevels(true); 

return jtaTransactionManager; 

2.3 MTAspect

package com.tonytaotao.dtsc.common.configuration.datasouce.jta; 

import com.tonytaotao.dtsc.common.utils.SpringContextUtil; 
import lombok.extern.slf4j.Slf4j; 
import org.aspectj.lang.ProceedingJoinPoint; 
import org.aspectj.lang.annotation.Around; 
import org.aspectj.lang.annotation.Aspect; 
import org.aspectj.lang.reflect.MethodSignature; 
import org.springframework.stereotype.Component; 
import org.springframework.transaction.jta.JtaTransactionManager; 

import javax.transaction.UserTransaction; 
import java.lang.reflect.Method; 
import java.util.Arrays; 

@Component 
@Aspect 
@Slf4j 
public class MTAspect { 

@Around(value = "@annotation(com.tonytaotao.dtsc.common.configuration.datasouce.jta.MultiTransactional)") 
public Object transactional(ProceedingJoinPoint point) throws Exception { 
String methodName = point.getSignature().getName(); 
Class[] parameterTypes = ((MethodSignature)point.getSignature()).getMethod().getParameterTypes(); 
UserTransaction userTransaction = null; 
Object result = null; 
MultiTransactional multiTransactional = null; 
try { 
Method method = point.getTarget().getClass().getMethod(methodName, parameterTypes); 

if (method.isAnnotationPresent(MultiTransactional.class)) { 
multiTransactional = method.getAnnotation(MultiTransactional.class); 
JtaTransactionManager transactionManager = SpringContextUtil.getBean(JtaTransactionManager.class); 
userTransaction = transactionManager.getUserTransaction(); 
userTransaction.begin(); 
log.warn(methodName + ", transaction begin"); 
result = point.proceed(); 
userTransaction.commit(); 
log.warn(methodName + ", transaction commit"); 


} catch (Throwable e) { 
log.error(e.getMessage(), e); 

if (userTransaction != null) { 
Class<? extends Throwable>[] rollbackExcptions = multiTransactional.rollbackFor(); 
Class<? extends Throwable>[] noRollbackExcptions = multiTransactional.noRollbackFor(); 
boolean rollback = isPresent(e, rollbackExcptions); 
boolean noRollback = isPresent(e, noRollbackExcptions); 

if (rollback || !noRollback) { 
userTransaction.rollback(); 
log.warn(methodName + ", transaction rollback"); 
} else { 
userTransaction.commit(); 
log.warn(methodName + ", transaction commit"); 




return result; 


private boolean isPresent(Throwable e, Class<? extends Throwable>[] excptions) { 
return Arrays.stream(excptions) 
.filter(exception -> e.getClass().isAssignableFrom(exception) || e.getClass().equals(exception)) 
.findAny() 
.isPresent(); 

2.4 MultiTransactional

package com.tonytaotao.dtsc.common.configuration.datasouce.jta; 

import org.springframework.core.annotation.AliasFor; 

import java.lang.annotation.*; 

@Target({ElementType.METHOD}) 
@Retention(RetentionPolicy.RUNTIME) 
@Inherited 
@Documented 
public @interface MultiTransactional { 

@AliasFor("transactionManager") 
String value() default ""; 

@AliasFor("value") 
String transactionManager() default ""; 

Class<? extends Throwable>[] rollbackFor() default {}; 

Class<? extends Throwable>[] noRollbackFor() default {}; 

2.5 MybatisConfig

package com.tonytaotao.dtsc.common.configuration.datasouce.jta; 

import com.tonytaotao.dtsc.common.configuration.datasouce.CustomSqlSessionTemplate; 
import com.tonytaotao.dtsc.common.configuration.datasouce.DBTypeEnum; 
import org.apache.ibatis.session.SqlSessionFactory; 
import org.mybatis.spring.SqlSessionFactoryBean; 
import org.mybatis.spring.annotation.MapperScan; 
import org.springframework.beans.factory.annotation.Qualifier; 
import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.Configuration; 
import org.springframework.core.io.support.PathMatchingResourcePatternResolver; 

import javax.sql.DataSource; 
import java.util.HashMap; 
import java.util.Map; 

@Configuration 
@MapperScan(basePackages = {"com.tonytaotao.dtsc.*.mapper"}, sqlSessionTemplateRef = "sqlSessionTemplate") 
public class MybatisConfig { 

@Bean("sqlSessionTemplate") 
public CustomSqlSessionTemplate sqlSessionTemplate(@Qualifier("xaSqlSessionFactoryOne") SqlSessionFactory xaSqlSessionFactoryOne, @Qualifier("xaSqlSessionFactoryTwo") SqlSessionFactory xaSqlSessionFactoryTwo) { 

CustomSqlSessionTemplate customSqlSessionTemplate = new CustomSqlSessionTemplate(xaSqlSessionFactoryOne); 

Map<Object, SqlSessionFactory> targetSqlSessionFactorys = new HashMap<>(); 
targetSqlSessionFactorys.put(DBTypeEnum.ONE.getCode(), xaSqlSessionFactoryOne); 
targetSqlSessionFactorys.put(DBTypeEnum.TWO.getCode(), xaSqlSessionFactoryTwo); 

customSqlSessionTemplate.setTargetSqlSessionFactorys(targetSqlSessionFactorys); 

return customSqlSessionTemplate; 


@Bean("xaSqlSessionFactoryOne") 
public SqlSessionFactory xaSqlSessionFactoryOne(@Qualifier("one") DataSource one) { 

try { 
return createSqlSessionFactory(one); 
} catch (Exception e) { 
e.printStackTrace(); 
return null; 



@Bean("xaSqlSessionFactoryTwo") 
public SqlSessionFactory xaSqlSessionFactoryTwo(@Qualifier("two") DataSource two) { 

try { 
return createSqlSessionFactory(two); 
} catch (Exception e) { 
e.printStackTrace(); 
return null; 



private SqlSessionFactory createSqlSessionFactory(DataSource dataSource) throws Exception{ 

SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); 
sqlSessionFactoryBean.setDataSource(dataSource); 
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:com/tonytaotao/dtsc/*/mapper/xml/*Mapper.xml")); 
sqlSessionFactoryBean.setTypeAliasesPackage("com.tonytaotao.dtsc.*.entity"); 

return sqlSessionFactoryBean.getObject(); 




2.6 CustomSqlSessionTemplate

package com.tonytaotao.dtsc.common.configuration.datasouce; 

import org.apache.ibatis.exceptions.PersistenceException; 
import org.apache.ibatis.executor.BatchResult; 
import org.apache.ibatis.reflection.ExceptionUtil; 
import org.apache.ibatis.session.*; 
import org.mybatis.spring.MyBatisExceptionTranslator; 
import org.mybatis.spring.SqlSessionTemplate; 
import org.mybatis.spring.SqlSessionUtils; 
import org.springframework.dao.support.PersistenceExceptionTranslator; 
import org.springframework.util.Assert; 

import java.lang.reflect.InvocationHandler; 
import java.lang.reflect.Method; 
import java.lang.reflect.Proxy; 
import java.sql.Connection; 
import java.util.List; 
import java.util.Map; 

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) Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(), 
new Class[] { SqlSession.class }, new SqlSessionInterceptor()); 
this.defaultTargetSqlSessionFactory = sqlSessionFactory; 


@Override 
public SqlSessionFactory getSqlSessionFactory() { 
SqlSessionFactory targetSqlSessionFactory = targetSqlSessionFactorys.get(DBContextHolder.getDbType()); 
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 = SqlSessionUtils.getSqlSession(CustomSqlSessionTemplate.this.getSqlSessionFactory(), 
CustomSqlSessionTemplate.this.executorType, CustomSqlSessionTemplate.this.exceptionTranslator); 
try { 
Object result = method.invoke(sqlSession, args); 
if (!SqlSessionUtils.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 = ExceptionUtil.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 { 
SqlSessionUtils.closeSqlSession(sqlSession, CustomSqlSessionTemplate.this.getSqlSessionFactory()); 



 

2.7  複製  https://my.oschina.net/TonyTaotao/blog/2980518  中的DBTypeEnum 、DBAnnotation 、DBAspect 、DBContextHolder 、DynamicDataSource 的配置。

3、使用方法

在需要跨庫寫操作的方法上加上 @MultiTransactional 註解,即可保證分佈式事務的一致性

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章