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是阻塞式的,所以不支持高并发,这个使用比较简单,一般用於单服务,多数据源的场景