分布式事务有很多博客,基本都是大同小异,这里只是记录一下,以后可以给自己参考

静态数据源版本

DruidConfig 核心配置

@Configuration
public class DruidConfig extends AbstractDataSourceConfig{@Bean(name = "master")@Primarypublic DataSource master(Environment env) throws SQLException {String prefix = "spring.datasource.druid.master.";return createAtomikosDataSourceBean(env, prefix, "master");}@Bean(name = "slave")public DataSource slave(Environment env) throws SQLException {String prefix = "spring.datasource.druid.slave.";return createAtomikosDataSourceBean(env, prefix, "slave");}@Bean(name = "xaTransactionManager")public JtaTransactionManager regTransactionManager () {UserTransactionManager userTransactionManager = new UserTransactionManager();UserTransaction userTransaction = new UserTransactionImp();return new JtaTransactionManager(userTransaction, userTransactionManager);}
}

MasterConfig

@Configuration
@MapperScan(basePackages = "com.avanty.mapper.master", sqlSessionFactoryRef = "sqlSessionFactoryMaster", sqlSessionTemplateRef = "sqlSessionTemplateMaster")
public class MasterConfig extends AbstractDataSourceConfig {@Autowired@Qualifier("master")private DataSource master;@Bean(name = "sqlSessionFactoryMaster")@Primarypublic SqlSessionFactory sqlSessionFactoryMaster()throws Exception {SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();sqlSessionFactoryBean.setDataSource(master);sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/master/*Mapper.xml"));//开启驼峰规则sqlSessionFactoryBean.getObject().getConfiguration().setMapUnderscoreToCamelCase(true);return sqlSessionFactoryBean.getObject();}@Bean(name = "sqlSessionTemplateMaster")@Primarypublic SqlSessionTemplate sqlSessionTemplateMaster(@Qualifier("sqlSessionFactoryMaster") SqlSessionFactory sqlSessionFactory) throws Exception {return new SqlSessionTemplate(sqlSessionFactory);}
}

Slave的配置跟上述代码雷同

测试

@RequestMapping("/hello2")@Transactionalpublic String hello2() {Map map1 = new HashMap();map1.put("name", "Zhan");map1.put("userAge", 18);test1Service.insertTest1(map1);Map map2 = new HashMap();map2.put("num", 6);map2.put("msg", "HelloTest");test2Service.insertTest2(map2);int i = 10 / 0;return "SUCCESS";}

配置durid监控

package com.avanty.app.dataSource;import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean;
import org.springframework.core.env.Environment;import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.Properties;public abstract class AbstractDataSourceConfig {protected DataSource createAtomikosDataSourceBean(Environment env, String prefix, String uniqueResourceName) throws SQLException {//        配置Druid监控DruidDataSource dataSource = new DruidDataSource();dataSource.setUrl(env.getProperty(prefix + "url"));dataSource.setUsername(env.getProperty(prefix + "username"));dataSource.setPassword(env.getProperty(prefix + "password"));Properties prop = build(env,prefix);AtomikosDataSourceBean ds = new AtomikosDataSourceBean();ds.setUniqueResourceName(uniqueResourceName);ds.setXaDataSourceClassName("com.alibaba.druid.pool.xa.DruidXADataSource");ds.setPoolSize(10);ds.setXaProperties(prop);return ds;}private Properties build(Environment env, String prefix) {Properties prop = new Properties();prop.put("url", env.getProperty(prefix + "url"));prop.put("username", env.getProperty(prefix + "username"));prop.put("password", env.getProperty(prefix + "password"));prop.put("driverClassName", env.getProperty(prefix + "driverClassName", ""));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("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("filters", env.getProperty(prefix + "filters"));return prop;}
}

动态数据源版本

代码在《SpringBoot+MyBatis 动态数据源(内附项目地址)》的基础上修改

动态数据源在使用@Transactional的时候会发现数据源没法切换,原因是在开启事务的时候,因为使用的是DataSourceTransactionManager, Spring取得数据源缓存到DataSourceTransactionObject对象中,用于commit, rollback等事务操作,虽然AbstractRoutingDataSource切换了数据源, 但是DataSourceTransactionManager还是没有更改过来,所以会出现问题

protected void doBegin(Object transaction, TransactionDefinition definition) {DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)transaction;Connection con = null;try {if (!txObject.hasConnectionHolder() || txObject.getConnectionHolder().isSynchronizedWithTransaction()) {Connection newCon = this.obtainDataSource().getConnection();if (this.logger.isDebugEnabled()) {this.logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");}txObject.setConnectionHolder(new ConnectionHolder(newCon), true);}txObject.getConnectionHolder().setSynchronizedWithTransaction(true);con = txObject.getConnectionHolder().getConnection();Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);txObject.setPreviousIsolationLevel(previousIsolationLevel);if (con.getAutoCommit()) {txObject.setMustRestoreAutoCommit(true);if (this.logger.isDebugEnabled()) {this.logger.debug("Switching JDBC Connection [" + con + "] to manual commit");}con.setAutoCommit(false);}this.prepareTransactionalConnection(con, definition);txObject.getConnectionHolder().setTransactionActive(true);int timeout = this.determineTimeout(definition);if (timeout != -1) {txObject.getConnectionHolder().setTimeoutInSeconds(timeout);}if (txObject.isNewConnectionHolder()) {TransactionSynchronizationManager.bindResource(this.obtainDataSource(), txObject.getConnectionHolder());}} catch (Throwable var7) {if (txObject.isNewConnectionHolder()) {DataSourceUtils.releaseConnection(con, this.obtainDataSource());txObject.setConnectionHolder((ConnectionHolder)null, false);}throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", var7);}}

关键文件修改

MultiDataSourceTransactionFactory

package com.avanty.dds;/*** @author: Michael Zhan* @description: MultiDataSourceTransactionFactory* @create: 2019-12-09 22:04:30*/
import org.apache.ibatis.session.TransactionIsolationLevel;
import org.apache.ibatis.transaction.Transaction;
import org.mybatis.spring.transaction.SpringManagedTransactionFactory;import javax.sql.DataSource;/*** <P>支持Service内多数据源切换的Factory</P>** @author lishuangqi* @date 2019/5/16 15:09* @since*/
public class MultiDataSourceTransactionFactory extends SpringManagedTransactionFactory {@Overridepublic Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit) {return new MultiDataSourceTransaction(dataSource);}
}

MultiDataSourceTransaction

package com.avanty.dds;/*** @author: Michael Zhan* @description: MultiDataSourceTransaction* @create: 2019-12-09 21:55:32*/import com.alibaba.druid.support.logging.Log;
import com.alibaba.druid.support.logging.LogFactory;
import org.apache.ibatis.transaction.Transaction;
import org.springframework.jdbc.CannotGetJdbcConnectionException;
import org.springframework.jdbc.datasource.DataSourceUtils;import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;/*** <P>多数据源切换,支持事务</P>** @author lishuangqi* @date 2019/5/16 15:09* @since*/
public class MultiDataSourceTransaction implements Transaction {private static final Log LOGGER = LogFactory.getLog(MultiDataSourceTransaction.class);private final DataSource dataSource;private Connection mainConnection;private String mainDatabaseIdentification;private ConcurrentMap<String, Connection> otherConnectionMap;private boolean isConnectionTransactional;private boolean autoCommit;public MultiDataSourceTransaction(DataSource dataSource) {this.dataSource = dataSource;otherConnectionMap = new ConcurrentHashMap<>();mainDatabaseIdentification = DynamicDataSourceContextHolder.getDataSourceKey();}/*** {@inheritDoc}*/@Overridepublic Connection getConnection() throws SQLException {String databaseIdentification = DynamicDataSourceContextHolder.getDataSourceKey();if (databaseIdentification.equals(mainDatabaseIdentification)) {if (mainConnection != null) return mainConnection;else {openMainConnection();mainDatabaseIdentification = databaseIdentification;return mainConnection;}} else {if (!otherConnectionMap.containsKey(databaseIdentification)) {try {Connection conn = dataSource.getConnection();otherConnectionMap.put(databaseIdentification, conn);} catch (SQLException ex) {throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex);}}return otherConnectionMap.get(databaseIdentification);}}private void openMainConnection() throws SQLException {this.mainConnection = DataSourceUtils.getConnection(this.dataSource);this.autoCommit = this.mainConnection.getAutoCommit();this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.mainConnection, this.dataSource);if (LOGGER.isDebugEnabled()) {LOGGER.debug("JDBC Connection ["+ this.mainConnection+ "] will"+ (this.isConnectionTransactional ? " " : " not ")+ "be managed by Spring");}}/*** {@inheritDoc}*/@Overridepublic void commit() throws SQLException {if (this.mainConnection != null && !this.isConnectionTransactional && !this.autoCommit) {if (LOGGER.isDebugEnabled()) {LOGGER.debug("Committing JDBC Connection [" + this.mainConnection + "]");}this.mainConnection.commit();for (Connection connection : otherConnectionMap.values()) {connection.commit();}}}/*** {@inheritDoc}*/@Overridepublic void rollback() throws SQLException {if (this.mainConnection != null && !this.isConnectionTransactional && !this.autoCommit) {if (LOGGER.isDebugEnabled()) {LOGGER.debug("Rolling back JDBC Connection [" + this.mainConnection + "]");}this.mainConnection.rollback();for (Connection connection : otherConnectionMap.values()) {connection.rollback();}}}/*** {@inheritDoc}*/@Overridepublic void close() throws SQLException {DataSourceUtils.releaseConnection(this.mainConnection, this.dataSource);for (Connection connection : otherConnectionMap.values()) {DataSourceUtils.releaseConnection(connection, this.dataSource);}}@Overridepublic Integer getTimeout() throws SQLException {return null;}
}

DataSourceConfigurer

@Bean(name = "sqlSessionFactory")@Primarypublic SqlSessionFactory sqlSessionFactory(Environment env)throws Exception {SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();// 配置数据源,此处配置为关键配置,如果没有将 dynamicDataSource 作为数据源则不能实现切换sqlSessionFactoryBean.setDataSource(dynamicDataSource(env));sqlSessionFactoryBean.setTransactionFactory(new MultiDataSourceTransactionFactory());sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*Mapper.xml"));return sqlSessionFactoryBean.getObject();}@Bean(name = "sqlSessionTemplate")@Primarypublic SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {return new SqlSessionTemplate(sqlSessionFactory);}

以上就是分布式事务的简单使用了


项目地址: https://gitee.com/ichampion/Public-Project-Demo.git

参考资料:《Spring分布式多动态数据源+事务》

《springboot+mybatis+druid+atomikos解决动态多数据源并支持分布式事务》

SpringBoot-Druid-Atomikos分布式事务相关推荐

  1. atomikos mysql,记一次 Atomikos 分布式事务的使用

    由于项目上的需要,我要同时往orcale数据库与sqlserver数据中插入数据,需要在一个事务之内完成这两个库的提交.参考了一下网上的各种JTA(Java Transaction API)实现之后, ...

  2. Atomikos 分布式事务的使用

    由于项目上的需要,我要同时往orcale数据库与sqlserver数据中插入数据,需要在一个事务之内完成这两个库的提交.参考了一下网上的各种JTA(Java Transaction API)实现之后, ...

  3. 使用atomikos分布式事务报com.atomikos.icatch.RollbackException: Prepare: NO vote异常解决办法

    发现后台使用atomikos进行事务提交时报javax.transaction.RollbackException: Prepare: NO vote,造成这个事务的主要原因是使用atomikos时, ...

  4. 分布式事务 (含面试题)- 图解 - 秒懂 - 史上最全

    文章很长,而且持续更新,建议收藏起来,慢慢读! Java 高并发 发烧友社群:疯狂创客圈(总入口) 奉上以下珍贵的学习资源: 免费赠送 经典图书 : 极致经典 + 社群大片好评 < Java 高 ...

  5. 分布式事务 (秒懂)

    文章很长,而且持续更新,建议收藏起来,慢慢读! Java 高并发 发烧友社群:疯狂创客圈(总入口) 奉上以下珍贵的学习资源: 免费赠送 经典图书 : 极致经典 + 社群大片好评 < Java 高 ...

  6. Spring Boot之基于Dubbo和Seata的分布式事务解决方案

    转载自 Spring Boot之基于Dubbo和Seata的分布式事务解决方案 1. 分布式事务初探 一般来说,目前市面上的数据库都支持本地事务,也就是在你的应用程序中,在一个数据库连接下的操作,可以 ...

  7. 3分钟搞定SpringBoot+Mybatis+druid多数据源和分布式事务

    在一些复杂的应用开发中,一个应用可能会涉及到连接多个数据源,所谓多数据源这里就定义为至少连接两个及以上的数据库了. 下面列举两种常用的场景: 一种是读写分离的数据源,例如一个读库和一个写库,读库负责各 ...

  8. SpringBoot+MyBatis(动态数据源/分布式事务XA(Atomikos))

    快速集成工具,欢迎打脸 说明 适用环境:SpringBoot / MyBatis / Atomickos  特性: 多数据源,动态切换 多数据源,XA分布式事务支持(需引入Atomickos) 仅支持 ...

  9. Spring-Boot + Atomikos 实现跨库的分布式事务管理

    一.何为事务 定义:事务是指多个操作单元组成的合集,多个单元操作是整体不可分割的,要么都操作成功,要么都不成功. 其必须遵循的四个原则(ACID): 原子性(Atomicity -- 美 [ˌætəˈ ...

  10. Spring中使用atomikos+druid实现经典分布式事务

    经典分布式事务,是相对互联网中的柔性分布式事务而言,其特性为ACID原则,包括原子性(Atomictiy).一致性(Consistency).隔离性(Isolation).持久性(Durabilit) ...

最新文章

  1. Java 8 Stream原理解析
  2. JQuery+ajax+jsonp 跨域访问
  3. Windows上安装MinGW(GCC),各个Package的作用
  4. Buy and Resell 2018中国大学生程序设计竞赛 - 网络选拔赛
  5. 写cookies注意事项
  6. 如何融合深度学习特征向量?
  7. Neginx服务搭建
  8. c# winForm使用Aspose.Cells读取CSV文件中文乱码问题
  9. 个人永久性免费-Excel催化剂功能第31波-数量金额分组凑数功能,财务表哥表姐最爱...
  10. 30. Element parentNode 属性
  11. 新版Excel和Word全屏打印预览的设定方法
  12. python ssim代码
  13. 工业相机和普通相机的区别
  14. 微信识图之面向多源异构数据的检测器设计
  15. stm32f103呼吸灯(PWM脉冲宽度调制)
  16. Matlab之format 设置命令行窗口输出显示格式
  17. android studio 自定义皮肤
  18. Java-构造方法(constructor)
  19. 党² - 李超线段树
  20. Javase day06_作业

热门文章

  1. 构建iOS持续集成平台(二)——测试框架
  2. 《程序设计基础》 第二章 用C语言编写程序 6-1 求排列数 (15 分)
  3. 返回值的意思,简单理解
  4. 苹果证书p12和描述文件的创建方法
  5. a到z的ascii码值是多少_a的ascii码值十六进制是多少
  6. 45天带你玩转Node(第一天)初探Node.js
  7. java try的用法_Java try和catch的使用
  8. 任意进制转任意进制 C++
  9. AHP层次分析法—特征权重初始化
  10. MatplotlibPandas教程