• springboot+atomikos+多数据源管理事务(mysql 8.0)


    jta:Java Transaction API,即是java中对事务处理的api 即 api即是接口的意思

    atomikos:Atomikos TransactionsEssentials 是一个为Java平台提供增值服务的并且开源类事务管理器

    1.结构

    2.pom依赖

    我这里使用本地数据库是mysql8,  

        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <!--<version>2.0.0.RELEASE</version>-->
            <version>2.1.0.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
    
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
            <java.version>1.8</java.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jdbc</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>1.3.0</version><!-- 1.3.0以上的版本没有@MapperScan以及@Select注解 -->
            </dependency>
            <!-- automatic+jta的分布式事务管理 -->
            <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-jta-atomikos -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jta-atomikos</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <scope>runtime</scope>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <!--boot 2.1默认 mysql8的版本; boot 2.0默认mysql5版本-->
                <version>8.0.13</version>
                <!--<version>5.1.46</version>-->
                <!--<scope>runtime</scope>-->
            </dependency>
    
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
                <version>1.18.2</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-configuration-processor</artifactId>
                <optional>true</optional>
            </dependency>
    
        </dependencies>

    3.创建本地数据库+表

    4.application.yml

    server:
      port: 8080
      servlet:
      #    # 项目contextPath
        context-path: /manyDatasource
    
    spring:
      application:
          name: manyDatasource
      datasource:
    #  spring.datasource.test1
    #    druid:
          test1:
    #      jdbc-url,url,jdbcurl哪个合适用哪个
            jdbcurl: jdbc:mysql://localhost:3306/test1?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8
            username: root
            password: 123456
            initial-size: 1
            min-idle: 1
            max-active: 20
            test-on-borrow: true
    #        driver-class-name: com.mysql.jdbc.Driver
    #        下面是最新的mysql8版本推荐的驱动
            driver-class-name: com.mysql.cj.jdbc.Driver
            type: com.alibaba.druid.pool.DruidDataSource
    #        下面是另外加的配置数据源的参数
            minPoolSize: 3
            maxPoolSize: 25
            maxLifetime: 20000
            borrowConnectionTimeout: 30
            loginTimeout: 30
            maintenanceInterval: 60
            maxIdleTime: 60
    
          test2:
            jdbcurl: jdbc:mysql://localhost:3306/test2?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8
            username: root
            password: 123456
            driver-class-name: com.mysql.cj.jdbc.Driver
            type: com.alibaba.druid.pool.DruidDataSource
            minPoolSize: 3
            maxPoolSize: 25
            maxLifetime: 20000
            borrowConnectionTimeout: 30
            loginTimeout: 30
            maintenanceInterval: 60
            maxIdleTime: 60
    
    mybatis:
        mapper-locations: classpath:mapper/*.xml
    
    #设置静态资源路径,多个以逗号分隔
    spring.resources.static-locations: classpath:static/,file:static/
    
    # 日志配置
    logging:
      level:
        czs: debug
        org.springframework: WARN
        org.spring.springboot.dao: debug

    5.实体类

     ps.使用lombok插件挺方便的~   id数据库主键自增

    @Data
    public class User {
        private Integer id;
        private String name;
        private long age;
    }

    6.mapper接口

    UserMapper1:
    public interface UserMapper1 {
        // 查询语句
        @Select("SELECT * FROM users WHERE NAME = #{name}")
        User findByName(@Param("name") String name);
    
        // 添加
        @Insert("INSERT INTO users(NAME, AGE) VALUES(#{name}, #{age})")
        int insert(@Param("name") String name, @Param("age") Integer age);
    }
    UserMapper2:

    public interface UserMapper2 {
        // 查询语句
        @Select("SELECT * FROM users WHERE NAME = #{name}")
        User findByName(@Param("name") String name);
    
        // 添加
        @Insert("INSERT INTO users(NAME, AGE) VALUES(#{name}, #{age})")
        int insert(@Param("name") String name, @Param("age") Integer age);
    }

    7.service

    ManyService1:
    @Service
    public class ManyService1 {
    
        @Autowired
        private UserMapper1 userMapper1;
        @Autowired
        private UserMapper2 userMapper2;
    
     /*   @Transactional(transactionManager = "test1TransactionManager",rollbackFor = Exception.class)
        public int insert(String name, Integer age) {
            int i = userMapper1.insert(name, age);
            System.out.println("userMapper1.insert结束~ :" + i);
           // int a = 1 / 0;//手动异常
            return i;
        }*/
    
        // 开启事务,由于使用jta+atomikos解决分布式事务,所以此处不必再指定事务
        @Transactional
        public int insert(String name, Integer age) {
            int insert = userMapper1.insert(name, age);
            //int i = 1 / age;// 赋值age为0故意引发事务
            return insert;
        }
    
        //http://localhost:8080/manyDatasource/insertDb1AndDb2?name=tom3&age=2
        // 开启事务,由于使用jta+atomikos解决分布式事务,所以此处不必再指定事务
        @Transactional
        public int insertDb1AndDb2(String name, Integer age) {
            int insert = userMapper1.insert(name, age);
            int insert2 = userMapper2.insert(name, age);
            int i = 1 / age;// 赋值age为0故意引发事务
            return insert + insert2;
        }
    
    
    }
    ManyService2:
    @Service
    public class ManyService2 {
    
        @Autowired
        private UserMapper2 userMapper2;
    
        @Transactional(transactionManager = "test2TransactionManager",rollbackFor = Exception.class)
        public int insert(String name, Integer age) {
            int i = userMapper2.insert(name, age);
            System.out.println("userMapper2.insert结束~ :" + null);
            int a = 1 / 0;//手动异常
            return i;
        }
    
    }
    
    

    8.Controller

    @RestController
    public class ManyController {
    
        @Autowired
        private ManyService1 manyService1;
    
        @Resource
        private ManyService2 manyService2;
    
        @RequestMapping(value = "datasource1")
        public int datasource1(String name, Integer age) {
            return manyService1.insert(name, age);
        }
    
        @RequestMapping(value = "datasource2")
        public int datasource2(String name, Integer age) {
            return manyService2.insert(name, age);
        }
    
        /**
         * @Param:
         * @Description: 这里测试两个service两个数据源的事务(不加上atomikos插件的情况下测试,
         *使用DataSource1Config和DataSource2Config 两个配置类, 关闭DBConfig1, DBConfig2和MyBatisConfig1, MyBatisConfig1两个类)
         * @Author: zyf    2019/5/10
         */
        //http://localhost:8080/manyDatasource/testManyTrans?name=tom4&age=2
        @RequestMapping(value = "testManyTrans")
        public int testManyTrans(String name, Integer age) {
            int i = 0;
            int i1 = manyService1.insert(name, age);
            System.out.println("manyService1.insert :" + i1);
    
            /*
            第二个事务中会手动造成一个异常~,
            但是第一个事务执行完毕了,保存到了数据库
            */
            int i2 = manyService2.insert(name, age);
            System.out.println("manyService2.insert :" + i2);
            return i;
        }
    
    
        /**
         * @Param:
         * @Description: 这里测试使用atomikos插件测试多数据源事务
         * @Author: zyf    2019/5/10
         */
        //http://localhost:8080/manyDatasource/insertDb1AndDb2?name=tom5&age=2
        //http://localhost:8080/manyDatasource/insertDb1AndDb2?name=tom6&age=0  //测试除数为0后的事务管理
        @RequestMapping(value = "insertDb1AndDb2")
        public int insertDb1AndDb2(String name, Integer age) {
            return manyService1.insertDb1AndDb2(name, age);
        }
    
    
    }

    9.配置数据源(*******重点总是在最后********)

    DBConfig1:
    @Data
    @ConfigurationProperties(prefix = "spring.datasource.test1") // 注意这个前缀要和application.yml文件的前缀一样
    public class DBConfig1 {
       // @Value("${mysql.datasource.test1.jdbcurl}")
       //@Value("${jdbcurl}")
        private String jdbcurl;
        //private String url;
        // 比如这个url在properties中是这样子的mysql.datasource.test1.username = root
        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;
    }
    DBConfig2:

    @Data
    @ConfigurationProperties(prefix = "spring.datasource.test2")// 注意这个前缀要和application.yml文件的前缀一样
    public class DBConfig2 {
        //@Value("${spring.datasource.test2.jdbcurl}")
        //@Value("${jdbcurl}")
        //private String url;
        private String jdbcurl;
        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;
    
    }

    上面两个配置类作用: 将application.yml配置文件中配置自动封装到实体类字段中,然后赋值给atomikos类型的数据源.(下面两个具体配置数据源)

    MyBatisConfig1:
    // 配置数据源
        //@Bean(name = "testDataSource")  //test1DataSource
        @Bean(name = "test1DataSource")  //test1DataSource
        public DataSource testDataSource(DBConfig1 testConfig) throws SQLException {
            MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
            //mysqlXaDataSource.setUrl(testConfig.getUrl());
            mysqlXaDataSource.setUrl(testConfig.getJdbcurl());
            mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
            mysqlXaDataSource.setPassword(testConfig.getPassword());
            mysqlXaDataSource.setUser(testConfig.getUsername());
            mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
    
            // 将本地事务注册到创 Atomikos全局事务
            AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
            xaDataSource.setXaDataSource(mysqlXaDataSource);
            xaDataSource.setUniqueResourceName("test1DataSource");
    
            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 = "test1SqlSessionFactory")
        public SqlSessionFactory testSqlSessionFactory(@Qualifier("test1DataSource") DataSource dataSource)
                throws Exception {
            SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
            bean.setDataSource(dataSource);
            return bean.getObject();
        }
    
        @Bean(name = "test1SqlSessionTemplate")
        public SqlSessionTemplate testSqlSessionTemplate(
                @Qualifier("test1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
            return new SqlSessionTemplate(sqlSessionFactory);
        }
    MyBatisConfig2 :
    @Configuration
    @MapperScan(basePackages = "czs.mapper2", sqlSessionTemplateRef = "test2SqlSessionTemplate")
    public class MyBatisConfig2 {
    
        // 配置数据源
        @Bean(name = "test2DataSource")
        public DataSource testDataSource(DBConfig2 testConfig) throws SQLException {
            MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
            //mysqlXaDataSource.setUrl(testConfig.getUrl());
            mysqlXaDataSource.setUrl(testConfig.getJdbcurl());
            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);
        }
    }

    10.测试

    http://localhost:8080/manyDatasource/insertDb1AndDb2?name=tom5&age=2   
    结果: test1和test2数据库都插入数据~
    http://localhost:8080/manyDatasource/insertDb1AndDb2?name=tom6&age=0   (两个insert操作后,手动异常)
    结果: test1和test2数据库都未插入数据~

     GitHub传送门: https://github.com/ColoZhu/springbootmanyDatasource

     参考出处: https://blog.csdn.net/qq_36138324/article/details/81612890

  • 相关阅读:
    单相、二相、三相区别
    Live for Speed 车模、赛道模型导出
    Lenovo/IBM Thinkpad X41 Tablet
    科普题外话 赛车性能的关键指标: 马力和扭力
    MAME™ Official Developer Documentation!
    Visual Studio 2005 & SQL Server 2005 are COMING!
    星际争霸(Star Craft)的Sprites导出
    推荐一本关于操作系统实践的好书
    科普题外话:Experimental Advanced Superconducting Tokamak - 人造太阳
    The Space Elevator -通往地球同步轨道的天梯
  • 原文地址:https://www.cnblogs.com/coloz/p/10845058.html
Copyright © 2020-2023  润新知