zoukankan      html  css  js  c++  java
  • 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%2B8
          username: root
          password: root
          borrowConnectionTimeout: 30
          loginTimeout: 30
          maintenanceInterval: 60
          maxIdleTime: 60
          maxLifetime: 20000
          maxPoolSize: 25
          minPoolSize: 3
          uniqueResourceName: springbootDatasource
    
        ###新数据库
        springbootdts:
          jdbc-url: jdbc:mysql://192.168.0.23:3306/springboot_dts?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
          username: root
          password: root
          borrowConnectionTimeout: 30
          loginTimeout: 30
          maintenanceInterval: 60
          maxIdleTime: 60
          maxLifetime: 20000
          maxPoolSize: 25
          minPoolSize: 3
          uniqueResourceName: springbootdtsDatasource

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

     SpringBootConfig

    package com.springboot.demo.model;
    
    import lombok.Data;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    
    @ConfigurationProperties(prefix = "spring.datasource.springboot")
    @Data
    public 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")
    @Data
    public 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.创建MysqlXADataSource
            MysqlXADataSource 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.创建MysqlXADataSource
            MysqlXADataSource 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

    后端报错

     查看数据库数据

     

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

    作者:彼岸舞

    时间:2021128

    内容关于:SpringBoot

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

  • 相关阅读:
    元素设置float属性后,其后面的元素的位置问题
    Vue.js经典开源项目汇总
    ES6-模块导入导出
    JavaScript内存泄漏
    父元素高度比子元素高度多几个像素的解决方法
    jasmine —— Spies(转)
    用npm-run自动化任务(转)
    AngularJS中页面传参方法
    Path模块部分常用函数解析——NodeJS
    html特殊字符表
  • 原文地址:https://www.cnblogs.com/flower-dance/p/14339901.html
Copyright © 2011-2022 走看看