mybatis第二话 - mybatis,多数据源的快乐你懂吗?

上篇文章讲述了mybatis的功能介绍以及springboot集成使用,但仅限于单个数据库,那么问题来了,我一个服务想连多个数据库怎么办呢?

1. 不得不说的话题,微服务时代为什么一个服务需要连多个数据库?

想必做过开发的都写过统计吧,虽然现在是内部调用各大服务查询的,但是如果能支持的话谁不想直接查询呢?
今天就来解决这个问题!

2. 预先准备

  • 数据库 demo_test、demo_test1
  • 同样的表,随便插入一条数据
CREATE TABLE `user_info` (
	`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
	`name` VARCHAR(50) NULL DEFAULT NULL,
	`age` INT(11) NULL DEFAULT NULL,
	`crad` VARCHAR(50) NULL DEFAULT NULL,
	`create_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP,
	`update_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
	PRIMARY KEY (`id`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
;
  • 项目架构
    在这里插入图片描述

和上篇文章的不同就是区分了Mapper接口和Mapper文件的位置,以此来模拟两个不同业务的数据源

3. pom mybatis依赖

<!--Mysql依赖包-->
<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<scope>runtime</scope>
</dependency>
<!--mybatis-->
<dependency>
	<groupId>org.mybatis.spring.boot</groupId>
	<artifactId>mybatis-spring-boot-starter</artifactId>
	<version>2.2.0</version>
</dependency>

4. yml配置文件

  • test和test1为两个不同的业务库
datasource:
  test:
  	#springboot 默认是Hikari数据源驱动 自动配置取的key名为jdbcUrl
  	#如果是Druid数据源驱动 自动配置取的key名为url
    jdbcUrl: jdbc:mysql://192.168.0.100:3306/demo_test?useUnicode=true&characterEncoding=UTF-8&useSSL=false
    url: jdbc:mysql://192.168.0.100:3306/demo_test?useUnicode=true&characterEncoding=UTF-8&useSSL=false
    username: root
    password: 123456
    driverClassName: com.mysql.cj.jdbc.Driver
  test1:
    jdbcUrl: jdbc:mysql://192.168.0.100:3306/demo_test1?useUnicode=true&characterEncoding=UTF-8&useSSL=false
    username: root
    password: 123456
    driverClassName: com.mysql.cj.jdbc.Driver

5. DataSource配置代码

//开启事务支持
@EnableTransactionManagement
@Configuration
//由于是配置多数据源 每个数据源的mapper由自己配置扫描 可以不用加@Mapper注解了
@MapperScan(basePackages = "com.example.demo.mapper.test", 
//指定数据源session工厂
sqlSessionFactoryRef = "testSqlSessionFactory")
public class TestDataSourceConfig {

    /**
     * 1.数据库连接配置
     * 2.数据库连接的工厂
     * 3.数据库连接的template
     * 4.数据库事务
     */

    //数据库连接配置
    @Bean("testDataSource")
    @ConfigurationProperties(prefix = "datasource.test")
    public DataSource testDataSource() {
    	//默认走的是Hikari
        return DataSourceBuilder.create().build();
    }

    //初始化连接工厂
    @Bean("testSqlSessionFactory")
    public SqlSessionFactory testSqlSessionFactory(@Qualifier("testDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
        //该数据源所管理的mapper xml文件
                .getResources("classpath:/mapper/test/*.xml"));
        return factoryBean.getObject();
    }

    //数据库template
    @Bean("testJdbcTemplate")
    public SqlSessionTemplate testJdbcTemplate(@Qualifier("testSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

    //声明事务 @Primary表明默认的事务回滚管理器
    //否则事务回滚时会抛异常 找到两个TransactionManager
    @Primary
    @Bean("testJdbcTransactionManager")
    public DataSourceTransactionManager testJdbcTransactionManager(@Qualifier("testDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

test1数据源的复制一份,声明的配置和bean name均修改成test1的就好

6. Mapper调用

  • TestAllMybatisController层调用
@Resource
TestMapper testMapper;
@Resource
Test1Mapper test1Mapper;

@GetMapping("/data1/select")
public Object select() {
    Map<String, Object> map = new HashMap<>();
    map.put("age", 19);
    map.put("ids", Arrays.asList(1));
    //数据
    List<Map<String, Object>> mapList = testMapper.select(map);
    List<Map<String, Object>> mapList1 = test1Mapper.select(map);
    map = new HashMap<>();
    map.put("test", mapList);
    map.put("test1", mapList1);
    return map;
}
  • 测试结果

{“test”:[{“update_time”:“2022-02-17T14:10:07”,“create_time”:“2022-02-16T15:05:40”,“name”:“张三1644996424601”,“id”:1,“crad”:“1645078200066”,“age”:19}],“test1”:[{“update_time”:“2022-02-17T14:49:42”,“create_time”:“2022-02-16T15:05:40”,“name”:“李四1644996424601”,“id”:1,“crad”:“1645080575838”,“age”:19}]}

可以看到查询是正常的,接下来来测试一下事务是否成功。

  • 事务测试代码
@Transactional
@GetMapping("/data1/update")
public Object update() {
    Map<String, Object> map = new HashMap<>();
    map.put("id", 1);
    map.put("name", "test");
    map.put("age", 222);
    map.put("crad", new Date().getTime());
    int i = testMapper.update(map);
    i = test1Mapper.update(map);
    //测试异常回滚
    int k = 1 / 0;
    return map;
}

经过测试,test数据库能回滚,但test1数据库正常提交,这是怎么一回事呢?
问题就出在事务管理,上面的@Priamry注解,声明test是默认的事务回滚器

同理使用@Transactional(value = "test1JdbcTransactionManager")测试,test1正常回滚,但是test又提交成功了
如果到这里能满足你的多数据源业务需求了,后面的就可以不用看了,接下来一起看下都想要回滚该怎么解决?

7. 多数据源分布式事务

7.1 pom新加依赖

<!-- druid数据源驱动 -->
<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>druid-spring-boot-starter</artifactId>
	<version>1.2.6</version>
</dependency>
<!--分布式事务管理-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>

7.2 yml配置修改

由于需要依赖druid数据源驱动,配置的数据库连接的key名需要修改为url

7.3 DataSourceConfig

@EnableTransactionManagement
@Configuration
@MapperScan(basePackages = "com.example.demo.mapper.test", sqlSessionFactoryRef = "testSqlSessionFactory")
public class TestDataSourceConfig {

    /**
     * 1.数据库连接配置
     * 2.数据库连接的工厂
     * 3.数据库连接的template
     * 4.数据库事务
     */

//    //数据库连接配置
//    @Bean("testDataSource")
//    @ConfigurationProperties(prefix = "datasource.test")
//    public DataSource testDataSource() {
//        return DataSourceBuilder.create().build();
//    }

    //初始化连接工厂
    @Bean("testSqlSessionFactory")
    public SqlSessionFactory testSqlSessionFactory(@Qualifier("testDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources("classpath:/mapper/test/*.xml"));
        return factoryBean.getObject();
    }

    //数据库template
    @Bean("testJdbcTemplate")
    public SqlSessionTemplate testJdbcTemplate(@Qualifier("testSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

//    //开启事务
//    @Primary
//    @Bean("testJdbcTransactionManager")
//    public DataSourceTransactionManager testJdbcTransactionManager(@Qualifier("testDataSource") DataSource dataSource) {
//        return new DataSourceTransactionManager(dataSource);
//    }

	//使用Druid管理数据池
    @Bean
    @ConfigurationProperties(prefix = "datasource.test")
    public DataSource druidDataSourceTest() {
        return new DruidXADataSource();
    }

	//封装了一层AtomikosDataSourceBean
    @Bean("testDataSource")
    public DataSource testDataSource(@Qualifier("druidDataSourceTest") DataSource druidDataSourceTest) {
        AtomikosDataSourceBean sourceBean = new AtomikosDataSourceBean();
        sourceBean.setXaDataSource((DruidXADataSource) druidDataSourceTest);
        //设置唯一的事务管理器名称
        sourceBean.setUniqueResourceName("test");
        return sourceBean;
    }
}

test1的代码就不贴了,Ctrl C V吧,注意别重复

7.4 @Transactional注解测试

大功告成,两个数据库都没有提交

以上就是本章的全部内容了。

上一篇:mybatis第一话 - mybatis,缘分让我们相遇
下一篇:mybatis第三话 - mybatis的高端用法你会吗?

天行健,君子以自强不息

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值