SpringBoot+Mybatis+atomikos-分布式事务+动态切换数据源

很久以前写过一片文章:SpringBoot+Mybatis-多数据源动态切换+动态加载,主要描述实现动态切换数据源,用到某个数据库的时候需要自己手动写代码切换, 但是最近公司业务需要,需要实现多个数据库的分布式事务,所以就有了本篇文章;注意流程:
(1) 在配置文件中配置数据库连接信息以及对应实体类.
(2) 配置多个数据库连接(数据源), 核心配置是注解@MapperScan(basePackages = {"com.mybatis.jta.demo.dao.car_impl*"}, sqlSessionTemplateRef = "sqlSessionTemplateCar"), 扫描自己数据库的dao接口, 使用自己的sqlSessionTemplate操作数据库,这样我们在写代码的时候就不需要手动切换数据库了.
(3) 配置事务管理器 .(协调多个数据源对事务进行提交或回滚)
(4) 编写代码获取spring容器中bean(连接数据源bean)
(5) 修改数据源bean中的数据库连接url等连接信息(动态切换数据源)

一.准备

  • java使用的是JTA方式实现的分布式事务, 如果想搞清楚原理的朋友请查看我的另外一篇文章: JTA分布式事务

  • 版本:重点是Mybatisdruid版本需要如下指定版本,因为Mybatis新版实现了jdbc4.1+的功能,而Druid的并没有实现,只是抛出了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
    
  • 三个数据库准备 db_test, db_user, ljyun_512_merchant, 存储sql语句的地址

     1. db_car 数据库有表 tb_message_package_no
     2. db_test 数据库有表 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>
          <!--分布式事务支持-->
          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-jta-atomikos</artifactId>
          </dependency>
    

二.application.properties中数据库配置和对应连接属性实体配置

  • application.properties中数据库配置
      # 数据库配置 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
    
      # 数据库配置 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
    
      # 动态创建的ljyun数据库
      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对应数据库配置实体代码位置
  • db_test对应数据库配置实体代码位置
  • ljyun_512_merchant对应数据库配置实体代码位置
  • 其实文件内容都是一样的,只是文件名不同而已;

三.数据源配置(很重要的一个环节)

  • db_car数据源配置 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数据源配置
       * 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 {
          //只能在三个数据源配置信息中出现一次,表示告诉spring初始化时候优先加载哪个数据源
          @Primary
          @Bean("dataSourceCarXA")
          public DruidXADataSource dataSourceXA(DataSourceCarProperties dataSourceCarProperties) {
          DruidXADataSource dataSource = new DruidXADataSource();
                  // 使用BeanUtils将数据库连接属性映射到数据源DruidXADataSource的属性中
          //当然也可以通过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数据源配置 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数据源配置
       * 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对应数据源配置 DataSourceYunConfig.java, 该数据源配置暂时连接的是ljyun_512_merchant数据库,下面会介绍通过参数动态修改该数据源的数据库连接信息,也就意味着切换数据库了.
          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私有库数据源配置
       * 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事务管理器配置类 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事务管理器配置类
       * 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和注解方式配置区别在于数据源配置文件中的 @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等业务代码编写外,核心配置已经完成.业务代码以及单元测试代码请查看我的代码库.单元测试请查看:TestIndexService.testMessageUser()方法,单元测试新增db_car.MessagePackageNodb_test.User表数据,固定数据源测试代码位置
  • 但是我们公司内部,需要通过外部参数来连接不同的数据库,也就是本文章中的ljyun_512_merchant有N个数据库,比如:ljyun_1_merchant,ljyun_2_merchant,ljyun_3_merchant,....,这就需要我们动态连接数据库.我的实现方式是通过外部参数动态修改spring容器中的ljyun_512_merchant数据源url,username,password等属性.接下来就是动态修改数据源bean属性方式实现动态切换数据源.

四.两个工具类配置

  • 自定义获取从容器中获取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);
          }
      }
    
  • 动态修改spring容器数据库连接池bean的url属性类 SwitchDB.java

      package com.mybatis.jta.demo.util;
      import com.alibaba.druid.pool.xa.DruidXADataSource;
      import lombok.extern.slf4j.Slf4j;
    
      /**
       * 动态修改数据库连接池的url属性类
       * Create by liangxifeng on 19-10-13
       */
      @Slf4j
      public class SwitchDB {
          /**
           * 修改spring容器中私有库数据源url信息
           * @param ljyunId 私有库云编号
           */
          public static void to(int ljyunId) {
          //从spring容器中获取数据源
          DruidXADataSource dataSource = (DruidXADataSource) SpringContextUtil.getBean("dataSourceYunXA");
          try {
              // 这里需要先关闭数据源,才可以使新修改的数据源设置生效
              dataSource.close();
          } catch (Exception e) {
              log.info("关闭数据源失败,连接url={}",dataSource.getUrl());
              e.printStackTrace();
          }
          String preUrl = "jdbc:mysql://localhost:3306/";
          String postUrl = "useUnicode=true&characterEncoding=utf8&useSSL=false";
          log.info("切换数据库ljyunId="+ljyunId);
          //修改数据源的连接url属性
          dataSource.setUrl(preUrl+"ljyun_"+ljyunId+"_merchant?" +postUrl);
          }
      }
    
  • 使用TestIndexService.testAddUserTags()方法单元测试,单元测新增db_test.User和ljyun_1_merchant.app_tag表数据, 注意事务存在与测试的service中.

          /**
       * 单元测试新增db_test.User和ljyun_512_merchant.app_tag表数据
       * 测试动态修改数据源的连接url属性,也就意味着切换数据库了
       * 在service中启用事务注解
       */
      @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()切换数据库

      /**
       * 分布式事务新增操作
       * @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表数据
          userDao.insert(user);
          //切换为ljyun_1_merchant数据库
          SwitchDB.to(1);
    
          //ljYun私有库数据库连接url信息
          System.out.println("yun数据库连接url="+((DruidXADataSource) SpringContextUtil.getBean("dataSourceYunXA")).getUrl());
          //db_car数据库连接url信息
          DruidXADataSource dataSourceCar = (DruidXADataSource) SpringContextUtil.getBean("dataSourceCarXA");
          System.out.println("db_car连接url="+dataSourceCar.getUrl());
          //db_test数据库连接url信息
          DruidXADataSource dataSourceTest = (DruidXADataSource) SpringContextUtil.getBean("dataSourceTestXA");
          System.out.println("db_test连接url="+dataSourceTest.getUrl());
          //int a = 10/0; //故意异常回滚
    
          //新增私有库.app_tag表数据
          appTagDao.insert(appTag);
          //手动回滚事务
          //TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
      }
    
  • 可以在业务代码中通过如下方式查看自己现在连接数据库的信息:
    以下代码中dataSourceYunXA就是数据源配置文件DataSourceCarConfig.java中对应的bean的名称.

        //ljYun私有库数据库连接url信息
        System.out.println("yun数据库连接url="+((DruidXADataSource) SpringContextUtil.getBean("dataSourceYunXA")).getUrl());
    

五. 错误与总结

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342