springboot+druid+mybatis+mysql+多數據源事務管理
分布式事務在java中的解決方案就是JTA(即Java Transaction API);springboot官方提供了 Atomikos or Bitronix的解決思路;其實,大多數情況下很多公司是使用消息隊列的方式實現分布式事務。這里分享的是Atomikos 的簡單事務管理。
項目依賴
pom.xml中添加atomikos的springboot相關依賴:
<!--分布式事務管理器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<!--這里最好要5.1.47之后的版本-->
<version>5.1.47</version>
</dependency>
application.properties配置文件中數據庫相關信息:
#數據庫1
spring.datasource.druid.one.url=jdbc:mysql://localhost:3306/test01?useUnicode=true&characterEncoding=utf-8
spring.datasource.druid.one.username=root
spring.datasource.druid.one.password=123456
#數據庫2
spring.datasource.druid.two.url=jdbc:mysql://localhost:3306/test02?useUnicode=true&characterEncoding=utf-8
spring.datasource.druid.two.username=root
spring.datasource.druid.two.password=123456
創建兩個java配置類,分別讀取上面的兩個數據庫相關信息:
@ConfigurationProperties(prefix = "spring.datasource.druid.one")
public class DsOneProperties {
private String username;
private String password;
private String url;
//這里省掉Set和get方法
}
@ConfigurationProperties(prefix = "spring.datasource.druid.two")
public class DsTwoProperties {
private String username;
private String password;
private String url;
//這里省掉Set和get方法
}
在SpringBoot項目啟動類加上注解,啟動時,就加載相關信息
@EnableConfigurationProperties(value = {DsOneProperties.class, DsTwoProperties.class})
創建主數據庫配置類MyBatisConfigOne :
@Configuration//聲明該類是一個配置類
@MapperScan(basePackages = "com.lwh.mybatistest.mapper", sqlSessionFactoryRef = "sqlSessionFactory1", sqlSessionTemplateRef = "sqlSessionTemplate1")
//掃描的包是com.lwh.mybatistest.mapper
public class MyBatisConfigOne {
// 配置主數據源
@Primary
@Bean
public DataSource dsOne(DsOneProperties dsOneProperties) throws SQLException {
//配置XA協議數據源,從配置文件中讀取相應屬性
MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
mysqlXaDataSource.setUrl(dsOneProperties.getUrl());
mysqlXaDataSource.setPassword(dsOneProperties.getPassword());
mysqlXaDataSource.setUser(dsOneProperties.getUsername());
//將本地事務注冊到Atomikos全局事務
AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
xaDataSource.setXaDataSource(mysqlXaDataSource);
xaDataSource.setUniqueResourceName("dsOne");
return xaDataSource;
}
@Primary
@Bean(name = "sqlSessionFactory1")
public SqlSessionFactory SqlSessionFactory1(@Qualifier("dsOne") DataSource dataSource)
throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
return bean.getObject();
}
@Primary
@Bean(name = "sqlSessionTemplate1")
public SqlSessionTemplate SqlSessionTemplate1(
@Qualifier("sqlSessionFactory1") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
依照上面主數據庫配置類,創建從數據庫配置類:
@Configuration
@MapperScan(basePackages = "com.lwh.mybatistest.mapper2", sqlSessionFactoryRef = "sqlSessionFactory2", sqlSessionTemplateRef = "sqlSessionTemplate2")
public class MyBatisConfigTwo {
@Bean
public DataSource dsTwo(DsTwoProperties dsTwoProperties) throws SQLException {
//配置從數據源
//配置XA協議數據源,從配置文件中讀取相應屬性
MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
mysqlXaDataSource.setUrl(dsTwoProperties.getUrl());
mysqlXaDataSource.setPassword(dsTwoProperties.getPassword());
mysqlXaDataSource.setUser(dsTwoProperties.getUsername());
//將本地事務注冊到Atomikos全局事務
AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
xaDataSource.setXaDataSource(mysqlXaDataSource);
xaDataSource.setUniqueResourceName("dsTwo");
return xaDataSource;
}
@Bean(name = "sqlSessionFactory2")
public SqlSessionFactory SqlSessionFactory2(@Qualifier("dsTwo") DataSource dataSource)
throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
return bean.getObject();
}
@Bean(name = "sqlSessionTemplate2")
public SqlSessionTemplate SqlSessionTemplate2(
@Qualifier("sqlSessionFactory2") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
創建一個簡單的controller測試類:
@RestController
@RequestMapping("/book")
public class BookController {
@Autowired
BookService bookService;
@Autowired
BookService2 bookService2;
@GetMapping("/add1")
@Transactional
public String addBook() {
Book book = new Book();
book.setBookname("測試");
book.setAuthor("test:01");
System.out.println("數據庫1:>>>>");
bookService.addBook(book);
System.out.println("數據庫2:>>>>");
bookService2.addBook(book);
return "測試add1操作成功!";
}
@GetMapping("/add2")
@Transactional
public String addBook2() {
Book book = new Book();
book.setBookname("測試add2");
book.setAuthor("test:01");
System.out.println("數據庫1:>>>>");
bookService.addBook(book);
int a = 10 / 0;
System.out.println("數據庫2:>>>>");
bookService2.addBook(book);
return "測試add2操作成功!";
}
}
Service類,就是簡單的插入方法,調用mapper:
@Service
public class BookService {
@Autowired
BookMapper bookMapper;
public void addBook(Book book) {
bookMapper.insertSelective(book);
}
}
springboot默認有事務管理器,所以這里沒有配置,使用默認的即可,如果有特別需求,可以自行創建自己的事務管理器。
最簡單的atomikos插件的使用就配置完了,配置信息相對簡單,想深入學習的同學,可以參考官方的文檔。
分布式事務有多種主流形態,包括:
基於消息實現的分布式事務
基於補償實現的分布式事務(gts/seata自動補償的形式)
基於TCC實現的分布式事務
基於SAGA實現的分布式事務
基於2PC實現的分布式事務
之所以有這么多形態,是因為任何事情都沒有銀彈,只有最合適當前場景的解決方案。