分佈式事務解決方案的問題,java
分佈式事務產生的緣由:mysql
多個不一樣的服務鏈接不一樣的數據源 ,作分佈式事務的管理。spring
這種狀況是鏈接兩個數據源的狀況,而後事務管理器是這樣的 只管理了test02的這端業務代碼。因此test02的這個會回滾!sql
可是test01會入庫哦apache
這屬於傳統的分佈式事務解決方案springboot
使用springboot+jta+atomikos 分佈式事物管理 (不適合微服務,須要拿到數據源而後註冊到同一個全局事務裏面去)session
好了廢話很少說,動手擼代碼!mybatis
引入的jar:app
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jta-atomikos</artifactId> </dependency>
配置文件也要修改:分佈式
application.properties:
# Mysql 1 mysql.datasource.test1.url = jdbc:mysql://localhost:3306/test01?useUnicode=true&characterEncoding=utf-8 mysql.datasource.test1.username = root mysql.datasource.test1.password = root mysql.datasource.test1.minPoolSize = 3 mysql.datasource.test1.maxPoolSize = 25 mysql.datasource.test1.maxLifetime = 20000 mysql.datasource.test1.borrowConnectionTimeout = 30 mysql.datasource.test1.loginTimeout = 30 mysql.datasource.test1.maintenanceInterval = 60 mysql.datasource.test1.maxIdleTime = 60 # Mysql 2 mysql.datasource.test2.url =jdbc:mysql://localhost:3306/test02?useUnicode=true&characterEncoding=utf-8 mysql.datasource.test2.username =root mysql.datasource.test2.password =root mysql.datasource.test2.minPoolSize = 3 mysql.datasource.test2.maxPoolSize = 25 mysql.datasource.test2.maxLifetime = 20000 mysql.datasource.test2.borrowConnectionTimeout = 30 mysql.datasource.test2.loginTimeout = 30 mysql.datasource.test2.maintenanceInterval = 60 mysql.datasource.test2.maxIdleTime = 60
底層原理是使用的2PC
創建Atomikos全局事務,全部的事務註冊進來
package com.toov5.configure; import org.springframework.boot.context.properties.ConfigurationProperties; import lombok.Data; @Data @ConfigurationProperties(prefix = "mysql.datasource.test1") public class DBConfig1 { private String url; private String username; private String password; private int minPoolSize; private int maxPoolSize; private int maxLifetime; private int borrowConnectionTimeout; private int loginTimeout; private int maintenanceInterval; private int maxIdleTime; private String testQuery; }
package com.toov5.configure; import org.springframework.boot.context.properties.ConfigurationProperties; import lombok.Data; //讀取解析配置文件,獲取值 @Data @ConfigurationProperties(prefix = "mysql.datasource.test2") public class DBConfig2 { private String url; private String username; private String password; private int minPoolSize; private int maxPoolSize; private int maxLifetime; private int borrowConnectionTimeout; private int loginTimeout; private int maintenanceInterval; private int maxIdleTime; private String testQuery; }
package com.toov5.datasource; import java.sql.SQLException; import javax.sql.DataSource; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionTemplate; 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 com.atomikos.jdbc.AtomikosDataSourceBean; import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource; import com.toov5.configure.DBConfig1; @Configuration // basePackages 最好分開配置 若是放在同一個文件夾可能會報錯 //跟以前都同樣。掃包而後指定工廠 //沒有配置事務管理器,至關於將本地事務註冊到Atomikos全局事務 @MapperScan(basePackages = "com.toov5.test01", sqlSessionTemplateRef = "testSqlSessionTemplate") public class MyBatisConfig1 { // 配置數據源 @Bean(name = "testDataSource") public DataSource testDataSource(DBConfig1 testConfig) throws SQLException { MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource(); mysqlXaDataSource.setUrl(testConfig.getUrl()); mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true); mysqlXaDataSource.setPassword(testConfig.getPassword()); mysqlXaDataSource.setUser(testConfig.getUsername()); mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true); //建立Atomikos全局事務,全部的事務註冊進來 AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean(); xaDataSource.setXaDataSource(mysqlXaDataSource); xaDataSource.setUniqueResourceName("testDataSource"); //testDataSource這個數據源的註冊 xaDataSource.setMinPoolSize(testConfig.getMinPoolSize()); xaDataSource.setMaxPoolSize(testConfig.getMaxPoolSize()); xaDataSource.setMaxLifetime(testConfig.getMaxLifetime()); xaDataSource.setBorrowConnectionTimeout(testConfig.getBorrowConnectionTimeout()); xaDataSource.setLoginTimeout(testConfig.getLoginTimeout()); xaDataSource.setMaintenanceInterval(testConfig.getMaintenanceInterval()); xaDataSource.setMaxIdleTime(testConfig.getMaxIdleTime()); xaDataSource.setTestQuery(testConfig.getTestQuery()); return xaDataSource; } @Bean(name = "testSqlSessionFactory") public SqlSessionFactory testSqlSessionFactory(@Qualifier("testDataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource); return bean.getObject(); } @Bean(name = "testSqlSessionTemplate") public SqlSessionTemplate testSqlSessionTemplate( @Qualifier("testSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception { return new SqlSessionTemplate(sqlSessionFactory); } }
package com.toov5.datasource; import java.sql.SQLException; import javax.sql.DataSource; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionTemplate; 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 com.atomikos.jdbc.AtomikosDataSourceBean; import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource; import com.toov5.configure.DBConfig2; @Configuration @MapperScan(basePackages = "com.toov5.test02", sqlSessionTemplateRef = "test2SqlSessionTemplate") public class MyBatisConfig2 { // 配置數據源 @Bean(name = "test2DataSource") public DataSource testDataSource(DBConfig2 testConfig) throws SQLException { MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource(); mysqlXaDataSource.setUrl(testConfig.getUrl()); mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true); mysqlXaDataSource.setPassword(testConfig.getPassword()); mysqlXaDataSource.setUser(testConfig.getUsername()); mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true); AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean(); xaDataSource.setXaDataSource(mysqlXaDataSource); xaDataSource.setUniqueResourceName("test2DataSource"); xaDataSource.setMinPoolSize(testConfig.getMinPoolSize()); xaDataSource.setMaxPoolSize(testConfig.getMaxPoolSize()); xaDataSource.setMaxLifetime(testConfig.getMaxLifetime()); xaDataSource.setBorrowConnectionTimeout(testConfig.getBorrowConnectionTimeout()); xaDataSource.setLoginTimeout(testConfig.getLoginTimeout()); xaDataSource.setMaintenanceInterval(testConfig.getMaintenanceInterval()); xaDataSource.setMaxIdleTime(testConfig.getMaxIdleTime()); xaDataSource.setTestQuery(testConfig.getTestQuery()); return xaDataSource; } @Bean(name = "test2SqlSessionFactory") public SqlSessionFactory testSqlSessionFactory(@Qualifier("test2DataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource); return bean.getObject(); } @Bean(name = "test2SqlSessionTemplate") public SqlSessionTemplate testSqlSessionTemplate( @Qualifier("test2SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception { return new SqlSessionTemplate(sqlSessionFactory); } }
package com.toov5.datasource; import java.sql.SQLException; import javax.sql.DataSource; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionTemplate; 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 com.atomikos.jdbc.AtomikosDataSourceBean; import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource; import com.toov5.configure.DBConfig2; @Configuration @MapperScan(basePackages = "com.toov5.test02", sqlSessionTemplateRef = "test2SqlSessionTemplate") public class MyBatisConfig2 { // 配置數據源 @Bean(name = "test2DataSource") public DataSource testDataSource(DBConfig2 testConfig) throws SQLException { MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource(); mysqlXaDataSource.setUrl(testConfig.getUrl()); mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true); mysqlXaDataSource.setPassword(testConfig.getPassword()); mysqlXaDataSource.setUser(testConfig.getUsername()); mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true); AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean(); xaDataSource.setXaDataSource(mysqlXaDataSource); xaDataSource.setUniqueResourceName("test2DataSource"); xaDataSource.setMinPoolSize(testConfig.getMinPoolSize()); xaDataSource.setMaxPoolSize(testConfig.getMaxPoolSize()); xaDataSource.setMaxLifetime(testConfig.getMaxLifetime()); xaDataSource.setBorrowConnectionTimeout(testConfig.getBorrowConnectionTimeout()); xaDataSource.setLoginTimeout(testConfig.getLoginTimeout()); xaDataSource.setMaintenanceInterval(testConfig.getMaintenanceInterval()); xaDataSource.setMaxIdleTime(testConfig.getMaxIdleTime()); xaDataSource.setTestQuery(testConfig.getTestQuery()); return xaDataSource; } @Bean(name = "test2SqlSessionFactory") public SqlSessionFactory testSqlSessionFactory(@Qualifier("test2DataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource); return bean.getObject(); } @Bean(name = "test2SqlSessionTemplate") public SqlSessionTemplate testSqlSessionTemplate( @Qualifier("test2SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception { return new SqlSessionTemplate(sqlSessionFactory); } }
package com.toov5.datasource; import java.sql.SQLException; import javax.sql.DataSource; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionTemplate; 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 com.atomikos.jdbc.AtomikosDataSourceBean; import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource; import com.toov5.configure.DBConfig2; @Configuration @MapperScan(basePackages = "com.toov5.test02", sqlSessionTemplateRef = "test2SqlSessionTemplate") public class MyBatisConfig2 { // 配置數據源 @Bean(name = "test2DataSource") public DataSource testDataSource(DBConfig2 testConfig) throws SQLException { MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource(); mysqlXaDataSource.setUrl(testConfig.getUrl()); mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true); mysqlXaDataSource.setPassword(testConfig.getPassword()); mysqlXaDataSource.setUser(testConfig.getUsername()); mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true); AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean(); xaDataSource.setXaDataSource(mysqlXaDataSource); xaDataSource.setUniqueResourceName("test2DataSource"); xaDataSource.setMinPoolSize(testConfig.getMinPoolSize()); xaDataSource.setMaxPoolSize(testConfig.getMaxPoolSize()); xaDataSource.setMaxLifetime(testConfig.getMaxLifetime()); xaDataSource.setBorrowConnectionTimeout(testConfig.getBorrowConnectionTimeout()); xaDataSource.setLoginTimeout(testConfig.getLoginTimeout()); xaDataSource.setMaintenanceInterval(testConfig.getMaintenanceInterval()); xaDataSource.setMaxIdleTime(testConfig.getMaxIdleTime()); xaDataSource.setTestQuery(testConfig.getTestQuery()); return xaDataSource; } @Bean(name = "test2SqlSessionFactory") public SqlSessionFactory testSqlSessionFactory(@Qualifier("test2DataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource); return bean.getObject(); } @Bean(name = "test2SqlSessionTemplate") public SqlSessionTemplate testSqlSessionTemplate( @Qualifier("test2SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception { return new SqlSessionTemplate(sqlSessionFactory); } }
package com.toov5.test02.service02; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.toov5.test01.mappertest01.UserMapperTest01; import com.toov5.test02.mappertest02.UserMapperTest02; import lombok.extern.slf4j.Slf4j; @Service @Slf4j public class UserService02 { @Autowired private UserMapperTest01 userMapperTest01; @Autowired private UserMapperTest02 userMapperTest02; @Transactionalpublic int test01Andtest02(String name, Integer age){ //鏈接兩個數據源 int result1 = userMapperTest01.insert(name, age); int result2 = userMapperTest02.insert(name, age); int i = 1/0; int result = result1+result2; return result; } }
目錄結構:
項目啓動類
package com.toov5.app; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.properties.EnableConfigurationProperties; import com.toov5.configure.DBConfig1; import com.toov5.configure.DBConfig2; @SpringBootApplication(scanBasePackages={"com.toov5.*"}) @EnableConfigurationProperties(value = { DBConfig1.class, DBConfig2.class }) //開啓讀取配置文件,啓動時候去讀取配置文件 public class app { public static void main(String[] args) { SpringApplication.run(app.class, args); } }