在之前的账单模块中, 有增删改查的方法. 但是在增加账单的时候, 只是简单的将账单导入数据库. 而假定该账单有绑定的账户, 则对应的账户余额也应该发生变化, 因此需要同时处理两张数据库表, 但是假设只是简单的将两个逻辑进行实现, 若是对账单的插入操作完毕后出现了异常还未执行账户的操作, 则就会出现已经写入了账单但是却没有修改账户余额的问题. 因此需要数据库的事务操作, 但是由于分布式的缘故, 因此需要分布式事务, 也就有了本片中需要使用的
Seata
Seata官网
本次的账本中使用的是1.4.2的版本, 也就是截至目前的最新版.
首先在官网进行对应版本的下载.
之后则需要进行对应的配置.
本次选用的是nacos注册中心 + nacos配置中心的组合.
有关nacos的使用, 可以参照: nacos官网
基本上是打开即用的级别. 对我这样的新手十分友好.
进入正题: 首先需要引入seata的依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<exclusions>
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.4.2</version>
</dependency>
<dependency>
<groupId>de.javakaffee</groupId>
<artifactId>kryo-serializers</artifactId>
<version>0.36</version>
</dependency>
这里先在starter中排除了seata-spring-boot-starter
这是因为需要避免版本的不统一, 先移除后再手动选择适合的版本进行引入(与服务器中开启的seata版本保持一致).
导入了依赖之后. 进入对应的seata的目录中, 编辑/conf/registry.conf文件进行相应的配置处理.
首先是注册中心registry板块.
type = "nacos"
nacos {
application = "seata-server"
serverAddr = "127.0.0.1:8848"
group = "SEATA_GROUP"
namespace = ""
cluster = "default"
username = "nacos"
password = "nacos"
}
本地开启的话, 最简单的就这么配即可.
其次, 是配置中心config板块:
同理可得:
type = "nacos"
nacos {
serverAddr = "127.0.0.1:8848"
namespace = ""
group = "SEATA_GROUP"
username = "nacos"
password = "nacos"
dataId = "service.vgroupMapping.bngel_tx_group"
}
其中需要注意的是, bngel_tx_group中的bngel换成你自定义的组名即可. 并没有特殊要求.
在文件中配完之后回到/bin目录下打开seata-server.bat(windows)即可运行.
记得要先打开
nacos
这里有一个坑点, 如果显示内存不足的话可以编辑
seata-server.bat文件, 修改其中的配置信息含有%JAVACMD% %JAVA_OPTS%的一行将2048改为1024或者更小即可.
运行成功后就是进行对应代码的配置.
在bootstrap.yml或者application.yml中添加如下配置:
seata:
tx-service-group: bngel_tx_group
enable-auto-data-source-proxy: false
service:
vgroup-mapping:
bngel_tx_group: default
client:
undo:
log-serialization: kryo
并且新增配置类:
@Configuration
public class DataSourceProxyConfig {
@Value("${mybatis.mapper-locations}")
private String mapperLocations;
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource druidDatasource() {
return new DruidDataSource();
}
@Bean
public DataSourceProxy dataSourceProxy(DataSource datasource) {
return new DataSourceProxy(datasource);
}
@Bean
@ConfigurationProperties(prefix = "mybatis.configuration")
public org.apache.ibatis.session.Configuration globalConfiguration() {
return new org.apache.ibatis.session.Configuration();
}
@Bean
public SqlSessionFactory sqlSessionFactory(DataSourceProxy dataSourceProxy, org.apache.ibatis.session.Configuration configuration) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSourceProxy);
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
sqlSessionFactoryBean.setConfiguration(configuration);
return sqlSessionFactoryBean.getObject();
}
}
在主启动类上加上注解
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
就可以关闭自动的数据源配置转而使用我们自定义的seata配置.
其中由于自动的配置失效的缘故, 对应的驼峰转换也会自动关闭.
在配置类中加上了对应prefix = "mybatis.configuration"的读取. 手动打开驼峰命名, 否则后续会出现一定的问题.
在`seata`配置完成并启动之后.接着便是业务逻辑相关的数据库分布式事务的编写.
目前的需求是(当`accountId`不为空的情况下):
1. 在新增账单时, 假如是收入的账单, 对应账户的余额增多, 若是支出则减少.
2. 在删除账单时, 账户进行相应余额的修改.
首先是使用openfeign操作account模块.
通过对应的service接口创建AccountService (同consumer模块)
之后在Impl类中进行使用.
@Autowired
private BillDao billDao;
@Autowired
private AccountService accountService;
@Override
@GlobalTransactional(name = "bngelbook-bill-save", rollbackFor = Exception.class)
public Integer saveBill(Bill bill) {
Integer result = billDao.saveBill(bill);
if (bill.getAccountId() != null) {
CommonResult<Account> commonResult = accountService.getAccountById(bill.getAccountId());
if (commonResult.getCode().equals(CommonResult.SUCCESS_CODE)) {
Account account = commonResult.getData();
Account newAccount = new Account();
newAccount.setId(account.getId());
newAccount.setBalance(account.getBalance() + ((bill.getIo() == 1) ? bill.getBalance() : -bill.getBalance()));
accountService.updateAccountById(newAccount);
}
}
return result;
}
此处以保存账本为例.
判断对应的account存在后, 获取该账户, 并且进行余额的修正.
在对应的方法上方使用@GlobalTransactional注解, 开启分布式事务.
name: 保证唯一性即可, 自定义.rollbackFor: 当发生某一异常时进行数据库回滚.Exception.class则表示只要有异常均回滚.
方法完成后, 运行Spring Boot. 与之前的方法并无二样. 可以直接使用. 就实现了对应的分布式事务处理.
欢迎各位来对我的小项目提出各种宝贵的意见, 这对我的进步非常重要, 谢谢大家.
GitHub地址: Bngel/bngelbook