spring cloud seata 使用示例
转账操作:转出账户表(account_out)、转入账户表(account_in)
**************************
客户端undo_log表
CREATE TABLE IF NOT EXISTS `undo_log`
(
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 'increment id',
`branch_id` BIGINT(20) NOT NULL COMMENT 'branch transaction id',
`xid` VARCHAR(100) NOT NULL COMMENT 'global transaction id',
`context` VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',
`rollback_info` LONGBLOB NOT NULL COMMENT 'rollback info',
`log_status` INT(11) NOT NULL COMMENT '0:normal status,1:defense status',
`log_created` DATETIME NOT NULL COMMENT 'create datetime',
`log_modified` DATETIME NOT NULL COMMENT 'modify datetime',
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDB
AUTO_INCREMENT = 1
DEFAULT CHARSET = utf8 COMMENT ='AT transaction mode undo table';
**************************
账户转出服务
******************
配置文件
application.yml
spring:
application:
name: account_out
cloud:
consul:
host: 192.168.57.21
port: 8500
datasource:
druid:
url: jdbc:mysql://192.168.57.10:3306/test2?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
mybatis-plus:
mapper-locations: classpath:/mapper/*.xml
seata:
application-id: account_out
tx-service-group: my_test_tx_group
registry:
type: consul
consul:
cluster: default
server-addr: 192.168.57.21:8500
config:
type: consul
consul:
server-addr: 192.168.57.21:8500
******************
config 层
DataConfig:开启数据源自动代理
@Configuration
@EnableAutoDataSourceProxy //使用注解开启数据源自动代理
@MapperScan("com.example.demo.dao")
public class DataConfig {
@Bean
public PaginationInterceptor initPaginationInterceptor(){
return new PaginationInterceptor();
}
@Bean
public OptimisticLockerInterceptor initOptimisticLockerInterceptor(){
return new OptimisticLockerInterceptor();
}
}
******************
controller 层
AccountOutController
@RestController
@RequestMapping("/accountOut")
public class AccountOutController {
@Resource
private AccountOutService accountOutService;
@RequestMapping("/get")
public Integer getAmount(@RequestParam("id") Integer id){
return accountOutService.getById(id).getAmount();
}
@RequestMapping("/transferOut")
public void transfer(@RequestParam("id") Integer id,@RequestParam("amount") Integer amount){
AccountOut accountOut=accountOutService.getById(id);
Integer a=accountOut.getAmount();
accountOut.setAmount(a-amount);
accountOutService.updateById(accountOut);
}
}
**************************
账户转入服务
******************
配置文件
application.yml
spring:
application:
name: account_in
cloud:
consul:
host: 192.168.57.21
port: 8500
datasource:
druid:
url: jdbc:mysql://192.168.57.10:3306/test1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
mybatis-plus:
mapper-locations: classpath:/mapper/*.xml
seata:
application-id: account_in
tx-service-group: my_test_tx_group
registry:
type: consul
consul:
cluster: default
server-addr: 192.168.57.21:8500
config:
type: consul
consul:
server-addr: 192.168.57.21:8500
******************
config 层
DataConfig:开启数据源自动代理
@Configuration
@EnableAutoDataSourceProxy
@MapperScan("com.example.demo.dao")
public class DataConfig {
@Bean
public PaginationInterceptor initPaginationInterceptor(){
return new PaginationInterceptor();
}
@Bean
public OptimisticLockerInterceptor initOptimisticLockerInterceptor(){
return new OptimisticLockerInterceptor();
}
}
******************
service 层
AccountOutService:账户转出服务,远程调用
@FeignClient(name = "account-out",fallback = AccountOutServiceFallback.class,path = "/accountOut")
public interface AccountOutService {
@RequestMapping("/get")
Integer getAmount(@RequestParam("id") Integer id);
@RequestMapping("/transferOut")
void transfer(@RequestParam("id") Integer id,@RequestParam("amount") Integer amount);
}
AccountInService:账户转入服务,本地调用
public interface AccountInService extends IService<AccountIn> {
void transferAccount(Integer id,Integer amount);
}
******************
serviceImpl 层
AccountOutServiceFallback:远程调用服务降级类
@Service
public class AccountOutServiceFallback implements AccountOutService {
@Override
public Integer getAmount(Integer id) {
return null;
}
@Override
public void transfer(Integer id, Integer amount) {
System.out.println("调用远程服务:accountOut出错");
}
}
AccountInServiceImpl
@Service
public class AccountInServiceImpl extends ServiceImpl<AccountInMapper, AccountIn> implements AccountInService {
@Resource
private AccountInMapper accountInMapper;
@Resource
private AccountOutService accountOutService;
@Override
@GlobalTransactional
public void transferAccount(Integer id,Integer amount) {
accountOutService.transfer(id,amount);
AccountIn accountIn =accountInMapper.selectById(id);
Integer a=accountIn.getAmount();
accountIn.setAmount(a+amount);
accountInMapper.updateById(accountIn);
if (amount<0){
throw new RuntimeException("amount 不能小于0");
}
}
}
******************
controller 层
AccountInController
@RestController
@RequestMapping("/accountIn")
public class AccountInController {
@Resource
private AccountInService accountInService;
@Resource
private AccountOutService accountOutService;
@RequestMapping("/transfer")
public void transferAccount(@RequestParam("id") Integer id,@RequestParam("amount") Integer amount){
System.out.println("转账前账户金额为:accountOut "+accountOutService.getAmount(id));
System.out.println("转账前账户金额为:accountIn "+accountInService.getById(id).getAmount());
try{
accountInService.transferAccount(id,amount);
System.out.println("转账成功");
}catch (Exception e){
System.out.println(e.getMessage());
}
System.out.println("转账后账户金额为:accountOut "+accountOutService.getAmount(id));
System.out.println("转帐后账户金额为:accountIn "+accountInService.getById(id).getAmount());
}
}
**************************
使用测试
192.168.57.23:8080/accountIn/transfer?id=1&amount=4
192.168.57.23:8080/accountIn/transfer?id=1&amount=-4
说明:amount为负,分布式事务回滚