文章目录  1.集成测试 1.集成测试正常下单 1.步骤 2.浏览器访问 http://localhost:10008/order/save?userId=666&productId=1&nums=1&money=100 3.注意事项和细节 2.集成测试模拟异常 1.步骤 1.com/sun/springcloud/controller/StorageController.java 休眠12s,模拟openfeign超时 2.仍然按照正常步骤启动并测试 3.预测结果 2.执行前数据库状态   3.浏览器输入 http://localhost:10008/order/save?userId=666&productId=1&nums=2&money=200,12s后查看数据库状态 order表 account表(这里在写sql时并没有计算数量,所以确实应该减去200) storage表(库存减2) 3.集成测试分布式事务控制 1.在com/sun/springcloud/service/Impl/OrderServiceImpl.java 的save方法加上@GlobalTransactional进行分布式事务控制 2.测试步骤 3.全部启动后查看nacos注册情况 4.测试前数据库状态 5.浏览器请求 http://localhost:10008/order/save?userId=666&productId=1&nums=1&money=100 5.注意事项 2.seata分布式事务总结 1.工作机制 2.seata的使用流程 1.安装 2.修改 D:\seata\conf\file.conf文件 1.修改事务组(需要进行分布式事务控制的微服务在同一组) 2.修改日志存储模式为db 3.修改数据库(MySQL5.7)连接信息 3.修改registry.conf 配置注册中心nacos(seata需要注册到nacos) 4.创建seata数据库和表 1.创建seata数据库 2.复制db_store.sql的内容,创建需要的表 5.启动nacos和seata进行测试,seata会注册到nacos 6.为每个需要分布式事务控制的数据库执行 db_undo_log.sql,创建undo_log表,用于事务回滚 7.pom.xml引入依赖 8.application.yml 9.D:\seata\conf下的两个配置文件复制到src/main/resources下并修改 1.将file.conf复制到src/main/resources下,然后修改这行,`seata的服务ip+端口`以及`db`根据实际情况修改(前面是修改过的) 2.registry.conf复制到src/main/resources下,然后配置注册中心nacos(前面也配过,直接复制即可) 3.最终的文件目录 10.进行业务逻辑编写 1.创建实体类 2.dao层 3.service层 4.controller层 11.两个常规配置类 1.MyBatisConfig.java 配置类,依赖注入所有Mapper接口(不用加@Mapper注解了) 2.DataSourceProxyConfig.java 配置数据源代理为 seata 12.创建主启动类(示例) 13.OpenFeign远程调用细节 1.在前面的依赖中已经引入了OpenFeign,并且在主启动类中也开启了Feign客户端 2.service接口声明需要远程调用的controller 3.远程调用的方式 4.谁可以远程调用   
 
 
 
 
 
 
 
 
 
 
保存订单,扣减账户余额,扣减库存成功!但是修改订单状态失败,也就是status是0! 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
create  database  seata; 
use  seata; 
 
 
 
注意这里引入的nacos是nacos-discovery starter,没有整合Seata之前引入的跟这个不一样!!!     < dependencies> < dependency> < groupId> </ groupId> < artifactId> </ artifactId> < optional> </ optional> </ dependency> < dependency> < groupId> </ groupId> < artifactId> </ artifactId> </ dependency> < dependency> < groupId> </ groupId> < artifactId> </ artifactId> </ dependency> < dependency> < groupId> </ groupId> < artifactId> </ artifactId> < exclusions> < exclusion> < artifactId> </ artifactId> < groupId> </ groupId> </ exclusion> </ exclusions> </ dependency> < dependency> < groupId> </ groupId> < artifactId> </ artifactId> < version> </ version> </ dependency> < dependency> < groupId> </ groupId> < artifactId> </ artifactId> </ dependency> < dependency> < groupId> </ groupId> < artifactId> </ artifactId> </ dependency> < dependency> < groupId> </ groupId> < artifactId> </ artifactId> </ dependency> < dependency> < groupId> </ groupId> < artifactId> </ artifactId> < version> </ version> </ dependency> < dependency> < groupId> </ groupId> < artifactId> </ artifactId> </ dependency> < dependency> < groupId> </ groupId> < artifactId> </ artifactId> </ dependency> < dependency> < groupId> </ groupId> < artifactId> </ artifactId> < optional> </ optional> </ dependency> < dependency> < groupId> </ groupId> < artifactId> </ artifactId> < scope> </ scope> </ dependency> < dependency> < groupId> </ groupId> < artifactId> </ artifactId> < version> </ version> </ dependency> </ dependencies> server : port :  10010  
spring : application : name :  seata- storage- micor- service cloud : alibaba : seata :   tx-service-group :  sun_order_tx_group nacos :  discovery : server-addr :  localhost: 8848 datasource :  driver-class-name :  com.mysql.jdbc.Driverurl :  username :  password :  
logging :   level : io : seata :  info
mybatis : mapperLocations :  classpath: mapper/*.xml  configuration : map-underscore-to-camel-case :  true  
seata的服务ip+端口以及db根据实际情况修改(前面是修改过的)
 
 
 
如果数据库中的数据是sun_name则在实体类中可以编写为sunName,不过这样需要在application.yml中开启自动驼峰命名(上面配置了) 这个就相当于在每个Mapper.xml中加了一个resultMap映射,如果实体类属性和表的字段可以被自动驼峰命名所映射那么就不需要再写resultMap Mapper接口注入容器 Mapper接口的@Param注解一旦指定,就不需要在xml实现的时候指定参数类型了,直接使用#{}来取出数据即可 Mapper.xml在application.yml中已经配置了自动扫描 service接口编写 service实现类注入容器 注入容器 在微服务中如果使用GetMapping则参数需要添加@RequestParam 如果使用PostMapping则参数需要添加@RequestBody package  com. sun. springcloud. config ; import  org. mybatis. spring. annotation.  MapperScan ; 
import  org. springframework. context. annotation.  Configuration ; 
@MapperScan ( "com.sun.springcloud.dao" ) 
@Configuration 
public  class  MyBatisConfig  { 
} 这里需要注意:配置文件中的mybatis.mapperLocations是以驼峰命名的 package  com. sun. springcloud. config ; import  com. alibaba. druid. pool.  DruidDataSource ; 
import  io. seata. rm. datasource.  DataSourceProxy ; 
import  org. apache. ibatis. session.  SqlSessionFactory ; 
import  org. mybatis. spring.  SqlSessionFactoryBean ; 
import  org. mybatis. spring. transaction.  SpringManagedTransactionFactory ; 
import  org. springframework. beans. factory. annotation.  Value ; 
import  org. springframework. boot. context. properties.  ConfigurationProperties ; 
import  org. springframework. context. annotation.  Bean ; 
import  org. springframework. context. annotation.  Configuration ; 
import  org. springframework. core. io. support.  PathMatchingResourcePatternResolver ; import  javax. sql.  DataSource ; 
@Configuration 
public  class  DataSourceProxyConfig  { @Value ( "${mybatis.mapperLocations}" ) 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  public  SqlSessionFactory  sqlSessionFactoryBean ( DataSourceProxy  dataSourceProxy) throws  Exception  { SqlSessionFactoryBean  sqlSessionFactoryBean = new  SqlSessionFactoryBean ( ) ; sqlSessionFactoryBean. setDataSource ( dataSourceProxy) ; sqlSessionFactoryBean. setMapperLocations( new  PathMatchingResourcePatternResolver ( ) . getResources ( mapperLocations) ) ;  sqlSessionFactoryBean. setTransactionFactory( new  SpringManagedTransactionFactory ( ) ) ; return  sqlSessionFactoryBean. getObject ( ) ; } 
} package  com. sun. springcloud ; import  org. springframework. boot.  SpringApplication ; 
import  org. springframework. boot. autoconfigure.  SpringBootApplication ; 
import  org. springframework. boot. autoconfigure. jdbc.  DataSourceAutoConfiguration ; 
import  org. springframework. cloud. client. discovery.  EnableDiscoveryClient ; 
import  org. springframework. cloud. openfeign.  EnableFeignClients ; 
@SpringBootApplication ( exclude =  { DataSourceAutoConfiguration . class } )  
@EnableDiscoveryClient  
@EnableFeignClients  
public  class  SeataStorageMicroServiceApplication10010  { public  static  void  main ( String [ ]  args)  { SpringApplication . run ( SeataStorageMicroServiceApplication10010 . class ,  args) ; } 
} 这里的FeignClient可以进行服务发现,得到要调用的服务的ip+端口+上下文路径 这里的RequestMapping可以找到远程调用的服务的资源路径,与服务发现的路径进行拼接即可找到指定资源 具体声明的方式就是 添加@FeignClient注解,指定要远程调用服务的application-name(注意不能带_) 将需要调用的controller直接复制过来,然后去掉方法体即可  package  com. sun. springcloud. service ; import  com. sun. springcloud. util.  Result ; 
import  org. springframework. cloud. openfeign.  FeignClient ; 
import  org. springframework. web. bind. annotation.  RequestMapping ; 
@FeignClient ( value =  "seata-storage-micor-service" ) 
public  interface  StorageService  { @RequestMapping ( "/storage/reduce" ) public  Result  reduce ( Long  productId,  Integer  nums) ; 
} 通过依赖注入针对service接口的代理对象进行远程调用