如果在一个项目中,配置了两个数据库,那么两个数据库如何配置,以及事务怎么控制?
这里我们使用jta-atomikos 来实现分布式事物管理。
引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>
针对使用多数据源,我们可以提供两种解决思路来实现。
1、AOP实现:在调用数据库的方法上,添加注解,执行此方法的时候,进行AOP拦截,然后根据注解信息选择用不同的数据源。
2、分包实现:不同的数据源,扫描不同的包。(本文章以Mybatis的MapperScan进行扫描示例)。
以下采用分包思路实现配置多数据源。
在单数据源中,我们只需要在配置文件中配置如下数据源即可,本文以application.properties配置为例
spring.datasource.url = jdbc:mysql://xxxxxxxx
spring.datasource.username = xxxxxx
spring.datasource.password = xxxxxx
#注意,新版本SpringBoot采用的横线代替驼峰
#spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.driverClassName = com.mysql.cj.jdbc.Driver
如果存在多个数据源,那么此时,如上配置则应变成
#mysql1配置
spring.datasource.mysql1.url = jdbc:mysql://xxxxxxxx
spring.datasource.mysql1.username = xxxxxx
spring.datasource.mysql1.password = xxxxxx
#mysql2配置
spring.datasource.mysql2.url = jdbc:mysql://xxxxxxxx
spring.datasource.mysql2.username = xxxxxx
spring.datasource.mysql2.password = xxxxxx
读取mysql1信息,mysql1信息和mysql2写法保持一直,mysql2配置就是将1换成2即可。
/**
* Description:映射application.properties中的mysql1信息到MysqlConfig1这个类中
* @author hutao
* @mail [email protected]
* @date 2020年8月8日
*/
@ConfigurationProperties(prefix = "mysql.datasource.mysql1")
public class MysqlConfig1 {
private String url;
private String username;
private String password;
//省略其他配置,及GET/SET
}
/**
* Description:映射application.properties中的mysql2信息到MysqlConfig2这个类中
* @author hutao
* @mail [email protected]
* @date 2020年8月8日
*/
@ConfigurationProperties(prefix = "mysql.datasource.mysql2")
public class MysqlConfig2 {
private String url;
private String username;
private String password;
//省略其他配置,及GET/SET
}
因此这里不是单数据源,不能使用系统自带的数据源配置,因此需要我们将配置的多数据源配置注入到Spring上下文。核心包含:DataSource 、SqlSessionFactory 、SqlSessionTemplate。
@SpringBootConfiguration
/**
* Description:多数据源分包扫描
* @author hutao
* @mail [email protected]
* @date 2020年8月8日
*/
@MapperScan(basePackages = "com.hutao.springboot.module.mysql1", sqlSessionTemplateRef = "sqlSessionTemplate1")
public class MyBatisConfig1 {
//@Primary:自动装配时当出现多个Bean候选者时,被注解为@Primary的Bean将作为首选者,否则将抛出异常
/**
* Description:配置数据源dataSource1,低版本的SpringBoot需要添加注解@Primary
* @author hutao
* @mail [email protected]
* @date 2020年8月8日
*/
@Bean(name = "dataSource1")
public DataSource dataSource1(MysqlConfig1 mysqlConfig) throws SQLException {
MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
mysqlXaDataSource.setUrl(mysqlConfig.getUrl());
mysqlXaDataSource.setPassword(mysqlConfig.getPassword());
mysqlXaDataSource.setUser(mysqlConfig.getUsername());
//省略其他mysql1配置信息
//jta-atomikos统一管理事务
AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
xaDataSource.setXaDataSource(mysqlXaDataSource);
xaDataSource.setUniqueResourceName("dataSource1");
//省略其他jta-atomikos配置
return xaDataSource;
}
/**
* Description:配置SQL会话工厂,低版本的SpringBoot需要添加注解@Primary
* @author hutao
* @mail [email protected]
* @date 2020年8月8日
*/
@Bean(name = "sqlSessionFactory1")
public SqlSessionFactory sqlSessionFactory1(@Qualifier("dataSource1") DataSource dataSource)
throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
return bean.getObject();
}
/**
* Description:配置SQL会话模板,低版本的SpringBoot需要添加注解@Primary
* @author hutao
* @mail [email protected]
* @date 2020年8月8日
*/
@Bean(name = "sqlSessionTemplate1")
public SqlSessionTemplate testSqlSessionTemplate(
@Qualifier("sqlSessionTemplate1") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
@SpringBootConfiguration
/**
* Description:多数据源分包扫描
* @author hutao
* @mail [email protected]
* @date 2020年8月8日
*/
@MapperScan(basePackages = "com.hutao.springboot.module.mysql2", sqlSessionTemplateRef = "sqlSessionTemplate2")
public class MyBatisConfig2 {
//@Primary:自动装配时当出现多个Bean候选者时,被注解为@Primary的Bean将作为首选者,否则将抛出异常
/**
* Description:配置数据源dataSource2,低版本的SpringBoot需要添加注解@Primary
* @author hutao
* @mail [email protected]
* @date 2020年8月8日
*/
@Bean(name = "dataSource2")
public DataSource dataSource2(MysqlConfig2 mysqlConfig) throws SQLException {
MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
mysqlXaDataSource.setUrl(mysqlConfig.getUrl());
mysqlXaDataSource.setPassword(mysqlConfig.getPassword());
mysqlXaDataSource.setUser(mysqlConfig.getUsername());
//省略其他mysql1配置信息
//jta-atomikos统一管理事务
AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
xaDataSource.setXaDataSource(mysqlXaDataSource);
xaDataSource.setUniqueResourceName("dataSource2");
//省略其他jta-atomikos配置
return xaDataSource;
}
/**
* Description:配置SQL会话工厂,低版本的SpringBoot需要添加注解@Primary
* @author hutao
* @mail [email protected]
* @date 2020年8月8日
*/
@Bean(name = "sqlSessionFactory2")
public SqlSessionFactory sqlSessionFactory1(@Qualifier("dataSource2") DataSource dataSource)
throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
return bean.getObject();
}
/**
* Description:配置SQL会话模板,低版本的SpringBoot需要添加注解@Primary
* @author hutao
* @mail [email protected]
* @date 2020年8月8日
*/
@Bean(name = "sqlSessionTemplate2")
public SqlSessionTemplate testSqlSessionTemplate(
@Qualifier("sqlSessionTemplate2") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
//启动加载数据库资源
@EnableConfigurationProperties(value = { MysqlConfig1 .class, MysqlConfig1 .class })
//声明Application这个类是这个项目的启动类
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
下述代码,当除0错误后,两个数据库都会回滚。
/**
* Description:多数据源分包扫描
* @author hutao
* @mail [email protected]
* @date 2020年8月8日
*/
@Transactional()
public int insertUserMysql1AndMysql2(String name, Integer age) {
// 传统分布式事务解决方案 jta+atomikos 注册同一个全局事务中
// 第一个数据源
int mysql1= userMapperMysql1.insert(name, age);
// 第二个数据源
int mysql1= userMapperMysql2.insert(name, age);
int i = 1 / 0;
return result;
}