AI智能
改变未来

SpringBoot之解决整合多数据源分布式事物问题


SpringBoot之解决整合多数据源分布式事物问题

概念:

  上一章只是解决了单事物问题,也就是说同时只能使用自己的数据源,并指定事物管理,才能使用,那么如果同时使用多个数据源,就会产生分布式事物问题

  分布式事物问题分两种:

    一种是这种一个项目多个数据源的分布式事物问题

    还有一种就是多个项目多个数据源之间的分布式事物问题

  这一章就来解决一下第一种一个项目多个数据源的分布式事物问题

编写接口:

这样的话就只能指定一个事物管理器,并不能两个数据库的事物都控制到,如果中间出现错误就会一个事物成功,一个事物失败,造成所谓的分布式事物问题

那么如何解决呢?

  采用一个统一的事物管理器来同时管理多个数据源

解决方案:

采用jta-atomikos

添加Maven依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jta-atomikos</artifactId></dependency>

修改配置文件也就是之前写的application-dts.yml

spring:datasource:###源数据库springboot:jdbc-url: jdbc:mysql://192.168.0.23:3306/springboot?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8username: rootpassword: rootborrowConnectionTimeout: 30loginTimeout: 30maintenanceInterval: 60maxIdleTime: 60maxLifetime: 20000maxPoolSize: 25minPoolSize: 3uniqueResourceName: springbootDatasource###新数据库springbootdts:jdbc-url: jdbc:mysql://192.168.0.23:3306/springboot_dts?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8username: rootpassword: rootborrowConnectionTimeout: 30loginTimeout: 30maintenanceInterval: 60maxIdleTime: 60maxLifetime: 20000maxPoolSize: 25minPoolSize: 3uniqueResourceName: springbootdtsDatasource

创建两个模型类用于存储两个配置

SpringBootConfig

package com.springboot.demo.model;import lombok.Data;import org.springframework.boot.context.properties.ConfigurationProperties;@ConfigurationProperties(prefix = \"spring.datasource.springboot\")@Datapublic class SpringBootConfig {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;private String uniqueResourceName;}

SpringBootDtsConfig

package com.springboot.demo.model;import lombok.Data;import org.springframework.boot.context.properties.ConfigurationProperties;@ConfigurationProperties(prefix = \"spring.datasource.springbootdts\")@Datapublic class SpringBootDtsConfig {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;private String uniqueResourceName;}

修改启动类

修改之前写的两个Mapper扫描配置类

修改后的SpringBootDataSourceConfig.java

package com.springboot.demo.config;import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;import com.springboot.demo.model.SpringBootConfig;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.boot.context.properties.ConfigurationProperties;import org.springframework.boot.jdbc.DataSourceBuilder;import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.jdbc.datasource.DataSourceTransactionManager;import javax.sql.DataSource;import java.sql.SQLException;@Configuration@MapperScan(basePackages = \"com.springboot.demo.springboot.mapper\", sqlSessionFactoryRef = \"springbootSqlSessionFactory\")public class SpringBootDataSourceConfig {/*** 将会员db注册到容器中** @return*/@Bean(name = \"springbootDataSource\")//    @ConfigurationProperties(prefix = \"spring.datasource.springboot\")public DataSource springbootDataSource(SpringBootConfig springBootConfig) throws SQLException {//        return DataSourceBuilder.create().build();// 1.创建MysqlXADataSourceMysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();mysqlXaDataSource.setUrl(springBootConfig.getUrl());mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);mysqlXaDataSource.setPassword(springBootConfig.getPassWord());mysqlXaDataSource.setUser(springBootConfig.getUserName());mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);// 2.将本地事务注册到创 Atomikos全局事务AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();xaDataSource.setXaDataSource(mysqlXaDataSource);xaDataSource.setUniqueResourceName(springBootConfig.getUniqueResourceName());xaDataSource.setMinPoolSize(springBootConfig.getMinPoolSize());xaDataSource.setMaxPoolSize(springBootConfig.getMaxPoolSize());xaDataSource.setMaxLifetime(springBootConfig.getMaxLifetime());xaDataSource.setBorrowConnectionTimeout(springBootConfig.getBorrowConnectionTimeout());xaDataSource.setLoginTimeout(springBootConfig.getLoginTimeout());xaDataSource.setMaintenanceInterval(springBootConfig.getMaintenanceInterval());xaDataSource.setMaxIdleTime(springBootConfig.getMaxIdleTime());xaDataSource.setTestQuery(springBootConfig.getTestQuery());return xaDataSource;}/*** 将会员SqlSessionFactory注册到容器中** @param dataSource* @return* @throws Exception*/@Bean(name = \"springbootSqlSessionFactory\")public SqlSessionFactory springbootSqlSessionFactory(@Qualifier(\"springbootDataSource\") DataSource dataSource) throws Exception {SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();sqlSessionFactoryBean.setDataSource(dataSource);return sqlSessionFactoryBean.getObject();}/*** 创建会员管理器** @param dataSource* @return*//*@Bean(name = \"springbootTransactionManager\")public DataSourceTransactionManager springbootTransactionManager(@Qualifier(\"springbootDataSource\") DataSource dataSource) {return new DataSourceTransactionManager(dataSource);}*//*** 创建订单sqlSesion模版** @param sqlSessionFactory* @return* @throws Exception*/@Bean(name = \"springbootSqlSessionTemplate\")public SqlSessionTemplate springbootSqlSessionTemplate(@Qualifier(\"springbootSqlSessionFactory\") SqlSessionFactory sqlSessionFactory) throws Exception {return new SqlSessionTemplate(sqlSessionFactory);}}

修改后的SpringBootDtsDataSourceConfig.java

package com.springboot.demo.config;import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;import com.springboot.demo.model.SpringBootDtsConfig;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.boot.context.properties.ConfigurationProperties;import org.springframework.boot.jdbc.DataSourceBuilder;import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.jdbc.datasource.DataSourceTransactionManager;import javax.sql.DataSource;import java.sql.SQLException;@Configuration@MapperScan(basePackages = \"com.springboot.demo.springbootdts.mapper\", sqlSessionFactoryRef = \"springbootdtsSqlSessionFactory\")public class SpringBootDtsDataSourceConfig {/*** 将会员db注册到容器中** @return*/@Bean(name = \"springbootdtsDataSource\")//    @ConfigurationProperties(prefix = \"spring.datasource.springbootdts\")public DataSource springbootdtsDataSource(SpringBootDtsConfig springBootDtsConfig) throws SQLException {//        return DataSourceBuilder.create().build();// 1.创建MysqlXADataSourceMysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();mysqlXaDataSource.setUrl(springBootDtsConfig.getUrl());mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);mysqlXaDataSource.setPassword(springBootDtsConfig.getPassWord());mysqlXaDataSource.setUser(springBootDtsConfig.getUserName());mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);// 2.将本地事务注册到创 Atomikos全局事务AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();xaDataSource.setXaDataSource(mysqlXaDataSource);xaDataSource.setUniqueResourceName(springBootDtsConfig.getUniqueResourceName());xaDataSource.setMinPoolSize(springBootDtsConfig.getMinPoolSize());xaDataSource.setMaxPoolSize(springBootDtsConfig.getMaxPoolSize());xaDataSource.setMaxLifetime(springBootDtsConfig.getMaxLifetime());xaDataSource.setBorrowConnectionTimeout(springBootDtsConfig.getBorrowConnectionTimeout());xaDataSource.setLoginTimeout(springBootDtsConfig.getLoginTimeout());xaDataSource.setMaintenanceInterval(springBootDtsConfig.getMaintenanceInterval());xaDataSource.setMaxIdleTime(springBootDtsConfig.getMaxIdleTime());xaDataSource.setTestQuery(springBootDtsConfig.getTestQuery());return xaDataSource;}/*** 将会员SqlSessionFactory注册到容器中** @param dataSource* @return* @throws Exception*/@Bean(name = \"springbootdtsSqlSessionFactory\")public SqlSessionFactory springbootdtsSqlSessionFactory(@Qualifier(\"springbootdtsDataSource\") DataSource dataSource) throws Exception {SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();sqlSessionFactoryBean.setDataSource(dataSource);return sqlSessionFactoryBean.getObject();}/*** 创建会员管理器** @param dataSource* @return*//* @Bean(name = \"springbootdtsTransactionManager\")public DataSourceTransactionManager springbootdtsTransactionManager(@Qualifier(\"springbootdtsDataSource\") DataSource dataSource) {return new DataSourceTransactionManager(dataSource);}*//*** 创建订单sqlSesion模版** @param sqlSessionFactory* @return* @throws Exception*/@Bean(name = \"springbootdtsSqlSessionTemplate\")public SqlSessionTemplate springbootdtsSqlSessionTemplate(@Qualifier(\"springbootdtsSqlSessionFactory\") SqlSessionFactory sqlSessionFactory) throws Exception {return new SqlSessionTemplate(sqlSessionFactory);}}

修改接口去除掉注解中指定的事物管理器

启动项目测试:

  启动的时候出现了一个小问题,找了一会,发现是yml配置文件和模型没有对上,修改一下jdbc-url改成url,因为是我们自己装载数据源就不用设置为jdbc-url了

再次启动,清空数据库,先测试正确的

访问报错:因为之前在user2接口上加了事物注解,并指定了事物管理器所以报错了,因为那个事物管理器已经不存在了,注释掉就可以

再次启动测试

测试成功..为什么是error呢,这是个失误因为我返回的就是error

查看数据库

成功了,接下来测试报错的

因为是写死的所以需要修改重启

测试输入num为0

后端报错

查看数据库数据

并没有插入,到此单项目多数据源分布式事物问题解决,开心…

作者:彼岸舞

时间:2021\\01\\28

内容关于:SpringBoot

本文来源于网络,只做技术分享,一概不负任何责任 

赞(0) 打赏
未经允许不得转载:爱站程序员基地 » SpringBoot之解决整合多数据源分布式事物问题