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")
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 註解,即可保證分散式事務的一致性