https://blog.csdn.net/chenjianhuideyueding/article/details/105273172
上一篇文章引入了兩個不同的數據源,操作不同的數據庫,現在修改下相應的服務實現類(加事務註解和異常代碼):
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
/**
* @author chenjianhui on 2020/04/02
*/
@Service
public class MultiDatasouceServiceImpl implements MultiDatasouceService {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Qualifier("jdbcTemplate0")
@Autowired
private JdbcTemplate jdbcTemplate0;
@Qualifier("jdbcTemplate1")
@Autowired
private JdbcTemplate jdbcTemplate1;
@Transactional
@Override
public void executeDatasource() {
jdbcTemplate1.update("INSERT INTO `multi_datasource1`.`black_user` (`app_type`, `user_pk`) VALUES ('1', '3')");
jdbcTemplate0.update("INSERT INTO `mutil_datasource0`.`black_user` (`app_type`, `user_pk`) VALUES ('1', '3')");
int i = 10;
i = i / 0;
}
}
執行之後,發現multi_datasource1數據庫操作成功,multi_datasource0操作失敗。
會出現這個情況是因爲,multi_datasource0加入到事務管理中,而multi_datasource1沒有,因爲在配置數據源的信息的時候,multi_datasource0是優先被考慮的數據源,所以這裏的@Transactional註解默認就是考慮multi_datasource0數據源,因此對其又事務的管理,發生異常時,進行事務的回滾;但是multi_datasource1沒有事務管理,所以是正常插入。
以下是使用atomikos進行分佈式事務的解決,代碼也上全吧(跟上述的描述已經完全沒關係了)
這個是分佈式事務的一個問題,要解決話,可以引入pom的依賴,比較上一篇發現多了atomikos的依賴:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.caraliu</groupId>
<artifactId>mutildatasource</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.0.RELEASE</version>
<relativePath/>
<!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>
</dependencies>
</project>
yml的配置:
##端口號
server:
port: 9999
##數據庫url
spring:
jpa:
database-platform: org.hibernate.dialect.MySQL5Dialect
datasource:
multiDataSource0:
url: jdbc:mysql://localhost:3306/mutil_datasource0?serverTimezone=GMT%2B8&characterEncoding=UTF-8&useSSL=false&rewriteBatchedStatements=true&zeroDateTimeBehavior=convertToNull
##數據庫用戶名
username: root
##數據庫密碼
password: root
##數據庫驅動
driver-class-name: com.mysql.jdbc.Driver
##數據庫url
multiDataSource1:
url: jdbc:mysql://localhost:3306/multi_datasource1?serverTimezone=GMT%2B8&characterEncoding=UTF-8&useSSL=false&rewriteBatchedStatements=true&zeroDateTimeBehavior=convertToNull
##數據庫用戶名
username: root
##數據庫密碼
password: root
##數據庫驅動
driver-class-name: com.mysql.jdbc.Driver
然後上一篇的JdbcTemplateConfig配置修改如下:
@Configuration
public class JdbcTemplateConfig {
@Value("${spring.datasource.multiDataSource0.url}")
private String db0Url;
@Value("${spring.datasource.multiDataSource0.username}")
private String db0Username;
@Value("${spring.datasource.multiDataSource0.password}")
private String db0Password;
@Value("${spring.datasource.multiDataSource1.url}")
private String db1Url;
@Value("${spring.datasource.multiDataSource1.username}")
private String db1Username;
@Value("${spring.datasource.multiDataSource1.password}")
private String db1Password;
@Bean(name = "dataSource0")
@Primary
public DataSource primaryDataSource() {
//設置 atomikos的數據源
AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();
atomikosDataSourceBean.setUniqueResourceName("primaryDataSource");
atomikosDataSourceBean.setXaDataSourceClassName(
"com.mysql.cj.jdbc.MysqlXADataSource");
Properties properties = new Properties();
properties.put("URL", db0Url);
properties.put("user", db0Username);
properties.put("password", db0Password);
atomikosDataSourceBean.setXaProperties(properties);
return atomikosDataSourceBean;
}
@Bean(name = "dataSource1")
public DataSource secondaryDataSource() {
AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();
atomikosDataSourceBean.setUniqueResourceName("secondaryDataSource");
atomikosDataSourceBean.setXaDataSourceClassName(
"com.mysql.cj.jdbc.MysqlXADataSource");
Properties properties = new Properties();
properties.put("URL", db1Url);
properties.put("user", db1Username);
properties.put("password", db1Password);
atomikosDataSourceBean.setXaProperties(properties);
return atomikosDataSourceBean;
}
@Primary
@Bean
JdbcTemplate jdbcTemplate0(@Qualifier("dataSource0") DataSource dataSource0) {
return new JdbcTemplate(dataSource0);
}
@Bean
JdbcTemplate jdbcTemplate1(@Qualifier("dataSource1") DataSource dataSource1) {
return new JdbcTemplate(dataSource1);
}
/**
* 注入事物管理器
* @return
*/
@Bean(name = "jtm")
public JtaTransactionManager regTransactionManager () {
UserTransactionManager userTransactionManager = new UserTransactionManager();
UserTransaction userTransaction = new UserTransactionImp();
return new JtaTransactionManager(userTransaction, userTransactionManager);
}
controller的修改:
@Controller
@RequestMapping(value = "/v1")
public class MultiDatasourceController {
@Autowired
private MultiDatasouceService multiDatasouceService;
@RequestMapping(value = "/mutilDatasource",method = RequestMethod.GET)
public ResponseEntity<?> mutilDatasource(){
multiDatasouceService.executeDatasource();
return new ResponseEntity<Object>(null, HttpStatus.OK);
}
}
MultiDatasouceServiceImpl實現類的修改:
@Service
public class MultiDatasouceServiceImpl implements MultiDatasouceService {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Qualifier("jdbcTemplate0")
@Autowired
private JdbcTemplate jdbcTemplate0;
@Qualifier("jdbcTemplate1")
@Autowired
private JdbcTemplate jdbcTemplate1;
// transactionManager = "jtm" 指明事務管理器
@Transactional(transactionManager = "jtm",rollbackFor = RuntimeException.class)
@Override
public void executeDatasource() {
jdbcTemplate1.update("INSERT INTO `multi_datasource1`.`black_user` (`app_type`, `user_pk`) VALUES ('1', '3')");
jdbcTemplate0.update("INSERT INTO `mutil_datasource0`.`black_user` (`app_type`, `user_pk`) VALUES ('1', '3')");
int i = 10;
i = i / 0;
}
}
啓動項目,執行請求發現:兩條數據都沒有插入,所以分佈式事務起作用了。
atomikos是基於XA協議的,就是2pc兩階段提交協議
第一階段: 涉及到同個事務中的數據庫數據源先詢問能否提交
第二階段:如果能,那麼提交事務;如果不行那麼就回滾事務
2pc是阻塞式的,所以不支持高併發,這個使用比較簡單,一般用於單服務,多數據源的場景