SpringBoot+Mybatis+atomikos-分布式事務(wù)+動態(tài)切換數(shù)據(jù)源

很久以前寫過一片文章:SpringBoot+Mybatis-多數(shù)據(jù)源動態(tài)切換+動態(tài)加載劫乱,主要描述實現(xiàn)動態(tài)切換數(shù)據(jù)源,用到某個數(shù)據(jù)庫的時候需要自己手動寫代碼切換, 但是最近公司業(yè)務(wù)需要荷腊,需要實現(xiàn)多個數(shù)據(jù)庫的分布式事務(wù),所以就有了本篇文章;注意流程:
(1) 在配置文件中配置數(shù)據(jù)庫連接信息以及對應(yīng)實體類.
(2) 配置多個數(shù)據(jù)庫連接(數(shù)據(jù)源), 核心配置是注解@MapperScan(basePackages = {"com.mybatis.jta.demo.dao.car_impl*"}, sqlSessionTemplateRef = "sqlSessionTemplateCar"), 掃描自己數(shù)據(jù)庫的dao接口, 使用自己的sqlSessionTemplate操作數(shù)據(jù)庫,這樣我們在寫代碼的時候就不需要手動切換數(shù)據(jù)庫了.
(3) 配置事務(wù)管理器 .(協(xié)調(diào)多個數(shù)據(jù)源對事務(wù)進(jìn)行提交或回滾)
(4) 編寫代碼獲取spring容器中bean(連接數(shù)據(jù)源bean)
(5) 修改數(shù)據(jù)源bean中的數(shù)據(jù)庫連接url等連接信息(動態(tài)切換數(shù)據(jù)源)

一.準(zhǔn)備

  • java使用的是JTA方式實現(xiàn)的分布式事務(wù), 如果想搞清楚原理的朋友請查看我的另外一篇文章: JTA分布式事務(wù)

  • 版本:重點是Mybatisdruid版本需要如下指定版本揍瑟,因為Mybatis新版實現(xiàn)了jdbc4.1+的功能,而Druid的并沒有實現(xiàn),只是拋出了SQLFeatureNotSupportedException異常;

    jdk:1.8
    mybatis-spring-boot-starter:1.3.4
    druid-spring-boot-starter: 1.1.14
    mysql-connector-java: 8.0.11
    spring-boot-starter-parent: 2.1.8.RELEASE
    
  • 三個數(shù)據(jù)庫準(zhǔn)備 db_test, db_user, ljyun_512_merchant, 存儲sql語句的地址

     1. db_car 數(shù)據(jù)庫有表 tb_message_package_no
     2. db_test 數(shù)據(jù)庫有表 tb_user, tb_role
     3. ljyun_512_merchant 商戶私有庫
    
  • pom.xml新增配置

          <!--使用阿里的Druid連接池-->
          <dependency>
              <groupId>com.alibaba</groupId>
              <artifactId>druid</artifactId>
              <version>1.1.14</version>
          </dependency>
          <!-- mybatis start -->
          <dependency>
              <groupId>org.mybatis.spring.boot</groupId>
              <artifactId>mybatis-spring-boot-starter</artifactId>
              <version>1.3.4</version>
          </dependency>
          <!--分布式事務(wù)支持-->
          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-jta-atomikos</artifactId>
          </dependency>
    

二.a(chǎn)pplication.properties中數(shù)據(jù)庫配置和對應(yīng)連接屬性實體配置

  • application.properties中數(shù)據(jù)庫配置
      # 數(shù)據(jù)庫配置 car
      spring.datasource.car.type=com.alibaba.druid.pool.DruidDataSource
      spring.datasource.car.driverClassName=com.mysql.cj.jdbc.Driver
      #spring.datasource.car.driverClassName=com.mysql.cj.jdbc.Driver
      spring.datasource.car.url=jdbc:mysql://localhost:3306/db_car?useUnicode=true&characterEncoding=utf8&useSSL\
        =false
      spring.datasource.car.username=root
      spring.datasource.car.password=123456
      spring.datasource.car.initialSize=5
      spring.datasource.car.minIdle=5
      spring.datasource.car.maxActive=20
      spring.datasource.car.maxWait=60000
      spring.datasource.car.timeBetweenEvictionRunsMillis=60000
      spring.datasource.car.minEvictableIdleTimeMillis=300000
      spring.datasource.car.validationQuery=SELECT 1 FROM DUAL
      spring.datasource.car.testWhileIdle=true
      spring.datasource.car.testOnBorrow=false
      spring.datasource.car.testOnReturn=false
      spring.datasource.car.poolPreparedStatements=true
      spring.datasource.car.maxPoolPreparedStatementPerConnectionSize=20
      spring.datasource.car.filters=stat,wall,log4j
      spring.datasource.car.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
    
      # 數(shù)據(jù)庫配置 test
      spring.datasource.test.type=com.alibaba.druid.pool.DruidDataSource
      spring.datasource.test.driverClassName=com.mysql.cj.jdbc.Driver
      spring.datasource.test.url=jdbc:mysql://localhost:3306/db_test?useUnicode=true&characterEncoding=utf8&useSSL=false
      spring.datasource.test.username=root
      spring.datasource.test.password=123456
      spring.datasource.test.initialSize=5
      spring.datasource.test.minIdle=5
      spring.datasource.test.maxActive=20
      spring.datasource.test.maxWait=60000
      spring.datasource.test.timeBetweenEvictionRunsMillis=60000
      spring.datasource.test.minEvictableIdleTimeMillis=300000
      spring.datasource.test.validationQuery=SELECT 1 FROM DUAL
      spring.datasource.test.testWhileIdle=true
      spring.datasource.test.testOnBorrow=false
      spring.datasource.test.testOnReturn=false
      spring.datasource.test.poolPreparedStatements=true
      spring.datasource.test.maxPoolPreparedStatementPerConnectionSize=20
      spring.datasource.test.filters=stat,wall,log4j
      spring.datasource.test.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
    
      # 動態(tài)創(chuàng)建的ljyun數(shù)據(jù)庫
      spring.datasource.yun.type=com.alibaba.druid.pool.DruidDataSource
      spring.datasource.yun.driverClassName=com.mysql.cj.jdbc.Driver
      spring.datasource.yun.url=jdbc:mysql://localhost:3306/ljyun_512_merchant?useUnicode=true&characterEncoding=utf8&useSSL\
        =false
      spring.datasource.yun.username=root
      spring.datasource.yun.password=123456
      spring.datasource.yun.initialSize=5
      spring.datasource.yun.minIdle=5
      spring.datasource.yun.maxActive=20
      spring.datasource.yun.maxWait=60000
      spring.datasource.yun.timeBetweenEvictionRunsMillis=60000
      spring.datasource.yun.minEvictableIdleTimeMillis=300000
      spring.datasource.yun.validationQuery=SELECT 1 FROM DUAL
      spring.datasource.yun.testWhileIdle=true
      spring.datasource.yun.testOnBorrow=false
      spring.datasource.yun.testOnReturn=false
      spring.datasource.yun.poolPreparedStatements=true
      spring.datasource.yun.maxPoolPreparedStatementPerConnectionSize=20
      spring.datasource.yun.filters=stat,wall,log4j
      spring.datasource.yun.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
    
  • db_car對應(yīng)數(shù)據(jù)庫配置實體代碼位置
  • db_test對應(yīng)數(shù)據(jù)庫配置實體代碼位置
  • ljyun_512_merchant對應(yīng)數(shù)據(jù)庫配置實體代碼位置
  • 其實文件內(nèi)容都是一樣的乍炉,只是文件名不同而已月培;

三.?dāng)?shù)據(jù)源配置(很重要的一個環(huán)節(jié))

  • db_car數(shù)據(jù)源配置 DataSourceCarConfig.java
      package com.mybatis.jta.demo.config;
      import com.alibaba.druid.pool.xa.DruidXADataSource;
      import com.atomikos.jdbc.AtomikosDataSourceBean;
      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.BeanUtils;
      import org.springframework.beans.factory.annotation.Qualifier;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.context.annotation.Primary;
      import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
      import javax.sql.DataSource;
    
      /**
       * db_car數(shù)據(jù)源配置
       * Create by liangxifeng on 19-9-27
       */
      @Configuration
      // 使用注解的方式使用
      //@MapperScan(basePackages = {"com.mybatis.jta.demo.mapper.car*"}, sqlSessionTemplateRef = "sqlSessionTemplateCar")
      @MapperScan(basePackages = {"com.mybatis.jta.demo.dao.car_impl*"}, sqlSessionTemplateRef = "sqlSessionTemplateCar") //
      // 掃描dao或mapper接口
      public class DataSourceCarConfig {
          //只能在三個數(shù)據(jù)源配置信息中出現(xiàn)一次,表示告訴spring初始化時候優(yōu)先加載哪個數(shù)據(jù)源
          @Primary
          @Bean("dataSourceCarXA")
          public DruidXADataSource dataSourceXA(DataSourceCarProperties dataSourceCarProperties) {
          DruidXADataSource dataSource = new DruidXADataSource();
                  // 使用BeanUtils將數(shù)據(jù)庫連接屬性映射到數(shù)據(jù)源DruidXADataSource的屬性中
          //當(dāng)然也可以通過dataSource.setUrl()的方式配置c
          BeanUtils.copyProperties(dataSourceCarProperties,dataSource);
          return dataSource;
          }
    
          @Bean(name = "dataSourceCar")
          public DataSource dataSourceCar(@Qualifier("dataSourceCarXA") DruidXADataSource dataSource){
          AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
          xaDataSource.setXaDataSource(dataSource);
          xaDataSource.setUniqueResourceName("dataSourceCar");
          return xaDataSource;
          }
    
          @Bean(name = "sqlSessionFactoryCar")
          public SqlSessionFactory sqlSessionFactoryCar(@Qualifier("dataSourceCar") DataSource dataSource)
              throws Exception {
          SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
          bean.setDataSource(dataSource);
          bean.setTypeAliasesPackage("com.mybatis.jta.demo.entity.car");
          bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:/mapper/car/*Mapper.xml"));
          return bean.getObject();
          }
    
          @Bean(name = "sqlSessionTemplateCar")
          public SqlSessionTemplate sqlSessionTemplateCar(
              @Qualifier("sqlSessionFactoryCar") SqlSessionFactory sqlSessionFactory) throws Exception {
          return new SqlSessionTemplate(sqlSessionFactory);
          }
      }
    
  • db_test數(shù)據(jù)源配置 DataSourceTestConfig.java
      package com.mybatis.jta.demo.config;
      import com.alibaba.druid.pool.xa.DruidXADataSource;
      import com.atomikos.jdbc.AtomikosDataSourceBean;
      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.BeanUtils;
      import org.springframework.beans.factory.annotation.Qualifier;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.context.annotation.Primary;
      import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
      import javax.sql.DataSource;
    
      /**
       * Description: db_test數(shù)據(jù)源配置
       * Create by liangxifeng on 19-9-27
       */
      @Configuration
      // 掃描dao或mapper接口
      //@MapperScan(basePackages = {"com.mybatis.jta.demo.mapper.test*"}, sqlSessionTemplateRef = "sqlSessionTemplateTest")
      @MapperScan(basePackages = {"com.mybatis.jta.demo.dao.test_impl*"}, sqlSessionTemplateRef = "sqlSessionTemplateTest") //
      public class DataSourceTestConfig {
    
          @Bean("dataSourceTestXA")
          public DruidXADataSource dataSourceXA(DataSourceTestProperties dataSourceTestProperties) {
          DruidXADataSource dataSource = new DruidXADataSource();
          BeanUtils.copyProperties(dataSourceTestProperties,dataSource);
          return dataSource;
          }
    
          @Bean(name = "dataSourceTest")
          public DataSource dataSourceTest(@Qualifier("dataSourceTestXA") DruidXADataSource dataSource){
          AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
          xaDataSource.setXaDataSource(dataSource);
          xaDataSource.setUniqueResourceName("dataSourceTest");
          return xaDataSource;
          }
    
          @Bean(name = "sqlSessionFactoryTest")
          public SqlSessionFactory sqlSessionFactoryTest(@Qualifier("dataSourceTest") DataSource dataSource)
              throws Exception {
          SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
          bean.setDataSource(dataSource);
          bean.setTypeAliasesPackage("com.mybatis.jta.demo.entity.test");
          bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:/mapper/test/*Mapper.xml"));
          return bean.getObject();
          }
    
          @Bean(name = "sqlSessionTemplateTest")
          public SqlSessionTemplate sqlSessionTemplateTest(
              @Qualifier("sqlSessionFactoryTest") SqlSessionFactory sqlSessionFactory) throws Exception {
          return new SqlSessionTemplate(sqlSessionFactory);
          }
      }
    
    
  • ljyun_512_merchnat對應(yīng)數(shù)據(jù)源配置 DataSourceYunConfig.java, 該數(shù)據(jù)源配置暫時連接的是ljyun_512_merchant數(shù)據(jù)庫恩急,下面會介紹通過參數(shù)動態(tài)修改該數(shù)據(jù)源的數(shù)據(jù)庫連接信息杉畜,也就意味著切換數(shù)據(jù)庫了.
          package com.mybatis.jta.demo.config;
      import com.alibaba.druid.pool.xa.DruidXADataSource;
      import com.atomikos.jdbc.AtomikosDataSourceBean;
      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.BeanUtils;
      import org.springframework.beans.factory.annotation.Qualifier;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.context.annotation.Primary;
      import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
      import javax.sql.DataSource;
    
      /**
       * ljyun私有庫數(shù)據(jù)源配置
       * Create by liangxifeng on 19-9-27
       */
      @Configuration
      // 使用注解的方式使用
      //@MapperScan(basePackages = {"com.mybatis.jta.demo.mapper.car*"}, sqlSessionTemplateRef = "sqlSessionTemplateCar")
      @MapperScan(basePackages = {"com.mybatis.jta.demo.dao.yun_impl*"}, sqlSessionTemplateRef = "sqlSessionTemplateYun") //
      // 掃描dao或mapper接口
      public class DataSourceYunConfig {
    
          @Bean("dataSourceYunXA")
          public DruidXADataSource dataSourceXA(DataSourceYunProperties dataSourceYunProperties) {
          DruidXADataSource dataSource = new DruidXADataSource();
          BeanUtils.copyProperties(dataSourceYunProperties,dataSource);
          return dataSource;
          }
    
          @Bean(name = "dataSourceYun")
          public DataSource dataSourceYun(@Qualifier("dataSourceYunXA") DruidXADataSource dataSource){
          AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
          xaDataSource.setXaDataSource(dataSource);
          xaDataSource.setUniqueResourceName("dataSourceYun");
          return xaDataSource;
          }
    
          @Bean(name = "sqlSessionFactoryYun")
          public SqlSessionFactory sqlSessionFactoryYun(@Qualifier("dataSourceYun") DataSource dataSource)
              throws Exception {
          SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
          bean.setDataSource(dataSource);
          bean.setTypeAliasesPackage("com.mybatis.jta.demo.entity.yun");
          bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources
                  ("classpath:/mapper/yun/*Mapper.xml"));
          return bean.getObject();
          }
    
          @Bean(name = "sqlSessionTemplateYun")
          public SqlSessionTemplate sqlSessionTemplateYun(
              @Qualifier("sqlSessionFactoryYun") SqlSessionFactory sqlSessionFactory) throws Exception {
          return new SqlSessionTemplate(sqlSessionFactory);
          }
      }
    
  • XA事務(wù)管理器配置類 XATransactionManagerConfig.java
          package com.mybatis.jta.demo.config;
      import com.atomikos.icatch.jta.UserTransactionImp;
      import com.atomikos.icatch.jta.UserTransactionManager;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.context.annotation.DependsOn;
      import org.springframework.transaction.PlatformTransactionManager;
      import org.springframework.transaction.annotation.EnableTransactionManagement;
      import org.springframework.transaction.jta.JtaTransactionManager;
    
      import javax.transaction.TransactionManager;
      import javax.transaction.UserTransaction;
      /**
       * XA事務(wù)管理器配置類
       * Create by liangxifeng on 19-9-27
       */
      @Configuration
      @EnableTransactionManagement
      public class XATransactionManagerConfig {
    
          @Bean(name = "userTransaction")
          public UserTransaction userTransaction() throws Throwable {
          UserTransactionImp userTransactionImp = new UserTransactionImp();
          //userTransactionImp.setTransactionTimeout(10000);
          return userTransactionImp;
          }
    
          @Bean(name = "atomikosTransactionManager", initMethod="init", destroyMethod = "close")
          //@Bean(name = "atomikosTransactionManager")
          public TransactionManager atomikosTransactionManager() throws Throwable {
          UserTransactionManager userTransactionManager = new UserTransactionManager();
          userTransactionManager.setForceShutdown(false);
          return userTransactionManager;
          }
    
          @Bean(name = "transactionManager")
          @DependsOn({ "userTransaction", "atomikosTransactionManager" })
          public PlatformTransactionManager transactionManager() throws Throwable {
          return new JtaTransactionManager(userTransaction(),atomikosTransactionManager());
          }
      }
    
  • 以上配置是基于mybatis配置文件的方式,其實xml和注解方式配置區(qū)別在于數(shù)據(jù)源配置文件中的 @MapperScan, 注解的方式掃描的是有注解的mapper接口目錄衷恭,xml方式掃描的是dao層接口目錄.xml的方式在sqlSessionFactory中有要配置setMapperLocations(xml配置文件路徑)
    • 基于注解方式配置如下:
      @MapperScan(basePackages = {"com.mybatis.jta.demo.mapper.car*"}, sqlSessionTemplateRef = "sqlSessionTemplateCar")
      @Bean(name = "sqlSessionFactoryCar")
      public SqlSessionFactory sqlSessionFactoryCar(@Qualifier("dataSourceCar") DataSource dataSource)
              throws Exception {
          SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
          bean.setDataSource(dataSource);
          bean.setTypeAliasesPackage("com.mybatis.jta.demo.entity.car");
          return bean.getObject();
      }
    
    • 基于xml的配置:
      @MapperScan(basePackages = {"com.mybatis.jta.demo.dao.car_impl*"}, sqlSessionTemplateRef = "sqlSessionTemplateCar")
      @Bean(name = "sqlSessionFactoryCar")
      public SqlSessionFactory sqlSessionFactoryCar(@Qualifier("dataSourceCar") DataSource dataSource)
              throws Exception {
          SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
          bean.setDataSource(dataSource);
          bean.setTypeAliasesPackage("com.mybatis.jta.demo.entity.car");
          bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:/mapper/car/*Mapper.xml"));
          return bean.getObject();
      }
    
  • 到此除了dao,service等業(yè)務(wù)代碼編寫外此叠,核心配置已經(jīng)完成.業(yè)務(wù)代碼以及單元測試代碼請查看我的代碼庫.單元測試請查看:TestIndexService.testMessageUser()方法,單元測試新增db_car.MessagePackageNodb_test.User表數(shù)據(jù),固定數(shù)據(jù)源随珠;測試代碼位置
  • 但是我們公司內(nèi)部灭袁,需要通過外部參數(shù)來連接不同的數(shù)據(jù)庫猬错,也就是本文章中的ljyun_512_merchant有N個數(shù)據(jù)庫,比如:ljyun_1_merchant,ljyun_2_merchant,ljyun_3_merchant茸歧,....倦炒,這就需要我們動態(tài)連接數(shù)據(jù)庫.我的實現(xiàn)方式是通過外部參數(shù)動態(tài)修改spring容器中的ljyun_512_merchant數(shù)據(jù)源url,username,password等屬性.接下來就是動態(tài)修改數(shù)據(jù)源bean屬性方式實現(xiàn)動態(tài)切換數(shù)據(jù)源.

四.兩個工具類配置

  • 自定義獲取從容器中獲取bean的工具類 SpringContextUtil

          package com.mybatis.jta.demo.util;
      import org.springframework.beans.BeansException;
      import org.springframework.context.ApplicationContext;
      import org.springframework.context.ApplicationContextAware;
      import org.springframework.stereotype.Component;
    
      /**
       * 自定義獲取從容器中獲取bean的工具類
       * liangxifeng 2019-10-13
       */
      @Component
      public class SpringContextUtil implements ApplicationContextAware {
    
          private static ApplicationContext applicationContext;
    
          @Override
          public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
          SpringContextUtil.applicationContext = applicationContext;
          }
    
          //獲取applicationContext
          public static ApplicationContext getApplicationContext() {
          return applicationContext;
          }
    
          //通過name獲取 Bean.
          public static Object getBean(String name) {
          return getApplicationContext().getBean(name);
          }
    
          //通過class獲取Bean.
          public static <T> T getBean(Class<T> clazz) {
          return getApplicationContext().getBean(clazz);
          }
    
          //通過name,以及Clazz返回指定的Bean
          public static <T> T getBean(String name, Class<T> clazz) {
          return getApplicationContext().getBean(name, clazz);
          }
      }
    
  • 動態(tài)修改spring容器數(shù)據(jù)庫連接池bean的url屬性類 SwitchDB.java

      package com.mybatis.jta.demo.util;
      import com.alibaba.druid.pool.xa.DruidXADataSource;
      import lombok.extern.slf4j.Slf4j;
    
      /**
       * 動態(tài)修改數(shù)據(jù)庫連接池的url屬性類
       * Create by liangxifeng on 19-10-13
       */
      @Slf4j
      public class SwitchDB {
          /**
           * 修改spring容器中私有庫數(shù)據(jù)源url信息
           * @param ljyunId 私有庫云編號
           */
          public static void to(int ljyunId) {
          //從spring容器中獲取數(shù)據(jù)源
          DruidXADataSource dataSource = (DruidXADataSource) SpringContextUtil.getBean("dataSourceYunXA");
          try {
              // 這里需要先關(guān)閉數(shù)據(jù)源,才可以使新修改的數(shù)據(jù)源設(shè)置生效
              dataSource.close();
          } catch (Exception e) {
              log.info("關(guān)閉數(shù)據(jù)源失敗,連接url={}",dataSource.getUrl());
              e.printStackTrace();
          }
          String preUrl = "jdbc:mysql://localhost:3306/";
          String postUrl = "useUnicode=true&characterEncoding=utf8&useSSL=false";
          log.info("切換數(shù)據(jù)庫ljyunId="+ljyunId);
          //修改數(shù)據(jù)源的連接url屬性
          dataSource.setUrl(preUrl+"ljyun_"+ljyunId+"_merchant?" +postUrl);
          }
      }
    
  • 使用TestIndexService.testAddUserTags()方法單元測試软瞎,單元測新增db_test.User和ljyun_1_merchant.app_tag表數(shù)據(jù), 注意事務(wù)存在與測試的service中.

          /**
       * 單元測試新增db_test.User和ljyun_512_merchant.app_tag表數(shù)據(jù)
       * 測試動態(tài)修改數(shù)據(jù)源的連接url屬性逢唤,也就意味著切換數(shù)據(jù)庫了
       * 在service中啟用事務(wù)注解
       */
      @Test
      public void testAddUserTags(){
          User user = new User();
          user.setCreateTime(null);
          user.setName("張三");
          user.setAge(10);
    
          AppTag appTag = new AppTag();
          appTag.setTagName("app_name_1");
          //appTag.setTagType(1);
          appTagService.insert(user,appTag);
      }
    

    appTagService.insert()中使用SwitchDB.to()切換數(shù)據(jù)庫

      /**
       * 分布式事務(wù)新增操作
       * @param user db_test.tb_user實體
       * @param appTag 私有庫app_tag實體
       */
      @Transactional
      public void insert(User user, AppTag appTag) {
          user.setCreateTime(null);
          //新增db_test.tb_user表數(shù)據(jù)
          userDao.insert(user);
          //切換為ljyun_1_merchant數(shù)據(jù)庫
          SwitchDB.to(1);
    
          //ljYun私有庫數(shù)據(jù)庫連接url信息
          System.out.println("yun數(shù)據(jù)庫連接url="+((DruidXADataSource) SpringContextUtil.getBean("dataSourceYunXA")).getUrl());
          //db_car數(shù)據(jù)庫連接url信息
          DruidXADataSource dataSourceCar = (DruidXADataSource) SpringContextUtil.getBean("dataSourceCarXA");
          System.out.println("db_car連接url="+dataSourceCar.getUrl());
          //db_test數(shù)據(jù)庫連接url信息
          DruidXADataSource dataSourceTest = (DruidXADataSource) SpringContextUtil.getBean("dataSourceTestXA");
          System.out.println("db_test連接url="+dataSourceTest.getUrl());
          //int a = 10/0; //故意異常回滾
    
          //新增私有庫.app_tag表數(shù)據(jù)
          appTagDao.insert(appTag);
          //手動回滾事務(wù)
          //TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
      }
    
  • 可以在業(yè)務(wù)代碼中通過如下方式查看自己現(xiàn)在連接數(shù)據(jù)庫的信息:
    以下代碼中dataSourceYunXA就是數(shù)據(jù)源配置文件DataSourceCarConfig.java中對應(yīng)的bean的名稱.

        //ljYun私有庫數(shù)據(jù)庫連接url信息
        System.out.println("yun數(shù)據(jù)庫連接url="+((DruidXADataSource) SpringContextUtil.getBean("dataSourceYunXA")).getUrl());
    

五. 錯誤與總結(jié)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末涤浇,一起剝皮案震驚了整個濱河市鳖藕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌只锭,老刑警劉巖著恩,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蜻展,居然都是意外死亡喉誊,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門纵顾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來裹驰,“玉大人,你說我怎么就攤上這事片挂』昧郑” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵音念,是天一觀的道長沪饺。 經(jīng)常有香客問我,道長闷愤,這世上最難降的妖魔是什么整葡? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮讥脐,結(jié)果婚禮上遭居,老公的妹妹穿的比我還像新娘。我一直安慰自己旬渠,他們只是感情好俱萍,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著告丢,像睡著了一般枪蘑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天岳颇,我揣著相機與錄音照捡,去河邊找鬼。 笑死话侧,一個胖子當(dāng)著我的面吹牛栗精,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播瞻鹏,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼悲立,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤饱搏,失蹤者是張志新(化名)和其女友劉穎辽狈,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體玩讳,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡涩蜘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了熏纯。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片同诫。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖樟澜,靈堂內(nèi)的尸體忽然破棺而出误窖,到底是詐尸還是另有隱情,我是刑警寧澤秩贰,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布霹俺,位于F島的核電站,受9級特大地震影響毒费,放射性物質(zhì)發(fā)生泄漏丙唧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一觅玻、第九天 我趴在偏房一處隱蔽的房頂上張望想际。 院中可真熱鬧,春花似錦溪厘、人聲如沸胡本。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽打瘪。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間闺骚,已是汗流浹背彩扔。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留僻爽,地道東北人虫碉。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像胸梆,于是被迫代替她去往敵國和親敦捧。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344