Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。
db_store.sql
脚本(seata1.0版本之前的conf配置文件下有)seata-server.bat
文件启动seata。可看到seata成功启动并注册到nacos中。@GlobalTransactional
注解)db_undo_log.sql
脚本(seata1.0版本之前的conf配置文件下有)新建seata-order-8001
、seata-storage-8002
、seata-account-8003
Module
下面以seata-order-8001
为例
seata-order-8001
Module<!-- nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- nacos -->
<!-- seata-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<exclusions>
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
<version>1.0.0</version>
</dependency>
<!-- seata-->
<!--feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--feign-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
server:
port: 8001
spring:
application:
name: seata-order
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 #nacos地址
alibaba:
seata:
tx-service-group: st_tx_group #seata事务组名称,对应file.conf文件
#数据库配置
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/seata_order?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Hongkong&useSSL=false
username: root
password: root
feign:
hystrix:
enabled: false
logging:
level:
io:
seata: info
mybatis:
mapper-locations: classpath:/mapper/*.xml
file.conf
到resources目录下egistry.conf
到resources目录下Order.java
和统一结果返回文件CommonResult.java
Order.java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Order {
private Long id;
private Long userId;
private Long productId;
private Integer count;
private BigDecimal money;
private Integer status;
}
CommonResult.java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T> {
private Integer code;
private String message;
private T date;
public CommonResult(Integer code, String message){
this(code,message,null);
}
}
OrderDao.java
@Mapper
public interface OrderDao {
void creat(Order order); //创建订单
void update(@Param("userId") Long usrId,@Param("status") Integer status); //修改订单状态
}
OrderMapper.xml
<mapper namespace="com.cloud.dao.OrderDao">
<resultMap id="BaseResultMap" type="com.cloud.dao.OrderDao">
<id column="id" property="id" jdbcType="BIGINT">id>
<result column="user_id" property="userId" jdbcType="BIGINT">result>
<result column="product_id" property="productId" jdbcType="BIGINT">result>
<result column="count" property="count" jdbcType="INTEGER">result>
<result column="status" property="status" jdbcType="INTEGER">result>
resultMap>
<insert id="creat">
insert into t_order(id,user_id,product_id,count,money,status) values
(null,#{userId},#{productId},#{count},#{money},0);
insert>
<update id="update">
update t_order set status=1
where user_id=#{userId} and status=#{status}
update>
mapper>
OrderService.java
public interface OrderService {
void creat(Order order);
}
AccountService.java
@Component
@FeignClient(value = "seata-account")
public interface AccountService {
@PostMapping(value = "/account/decrease")
CommonResult decrease(@RequestParam("userId") Long userId, @RequestParam("money") BigDecimal money);
}
StorageService.java
@Component
@FeignClient(value = "seata-storage")
public interface StorageService {
@PostMapping(value = "/storage/decrease")
CommonResult decrease(@RequestParam("productId") Long productId,@RequestParam("count") Integer count);
}
OrderServiceImpl.java
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {
@Resource
private OrderDao orderDao;
@Resource
private AccountService accountService;
@Resource
private StorageService storageService;
@Override
public void creat(Order order) {
log.info("-->创建订单");
orderDao.creat(order);
log.info("-->减少库存");
storageService.decrease(order.getProductId(),order.getCount());
log.info("-->减少账号余额");
accountService.decrease(order.getUserId(),order.getMoney());
log.info("-->修改订单状态");
orderDao.update(order.getUserId(),0);
log.info("-->结束");
}
@RestController
public class OrderController {
@Resource
private OrderService orderService;
@GetMapping("/order/create")
public CommonResult create(Order order){
orderService.creat(order);
return new CommonResult(200,"创建成功");
}
}
MyBatisConfig.java
@Configuration
@MapperScan({"com.cloud.dao"})
public class MyBatisConfig {
}
DataSourceProxyConfig.java
//使用seata对数据源代理
@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
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();
}
}
@EnableDiscoveryClient
@EnableFeignClients
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)//取消数据源的自动创建
public class SeataOrderMain {
public static void main(String[] args) {
SpringApplication.run(SeataOrderMain.class,args);
}
}
@GlobalTransactional
注解@RequestMapping("/storage/decrease")
public CommonResult decrease(@RequestParam("productId") Long productId, @RequestParam("count") Integer count) throws InterruptedException {
TimeUnit.SECONDS.sleep(3); //等待3秒
storageService.decrease(productId,count);
return new CommonResult(200,"库存减少成功");
}
@GlobalTransactional
注解OrderServiceImpl.java
的creat方法上加上@GlobalTransactional
注解,同样库存服务做超时处理,数据库数据恢复初始设置。
@Override
@GlobalTransactional
public void creat(Order order) {
log.info("-->创建订单");
orderDao.creat(order);
log.info("-->减少库存");
storageService.decrease(order.getProductId(),order.getCount());
log.info("-->减少账号余额");
accountService.decrease(order.getUserId(),order.getMoney());
log.info("-->修改订单状态");
orderDao.update(order.getUserId(),0);
log.info("-->结束");
}
创建订单报错
数据库此时三张表并未发生改变
订单表t_order
未发生改变
库存表t_storage
未发生改变
账户表t_account
未发生改变