上篇文章讲述了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的高端用法你会吗?
天行健,君子以自强不息