单体架构多数据源事务解决方案


演示源码github:链接

1. 本文适合场景

本文事务解决方案只适用于单体架构项目多数据源的场景,就是同一进程中多个数据源要想解决事务问题可以使用本方案。

1.1 场景介绍

比如说我们现在做一个出行项目,类似滴滴出行这样子的,然后呢项目是个单体架构,但是数据却存在了不同的库中了,比如说与司机有关的表都放在了driver_db 库中了,与订单有关的表都存放在了order_db 库中了,项目是使用配置多数据源的形式来操作数据库的。
现在,有个用户a下了一个用车订单,然后系统在order_db 库中的order_info 表中插入一条订单信息,下图:
在这里插入图片描述
id_ 是我们的订单编号,driver_id 是接单司机,create_time 就是咱们的创建时间,剩下的字段都省略掉…
接着driver_id为1的司机抢到了这个订单,这时候,需要在司机表中有个当前订单字段中填入 订单号,在订单信息中填入接单司机。
我们来看看这个司机表
在这里插入图片描述
id_是司机id,dirver_name 这个这里用不到,curr_order_id 是当前接到的订单,create_time司机注册时间。

这里咱们有这么一个业务要求就是: 司机接到订单的时候,需要订单信息要添加driver_id,司机信息中的curr_order_id要添加订单编号。
这两步要同时执行,保持一致性,要么都成功,要么都失败,否则会对影响业务,比如说driver_id 为1的司机抢到为1的订单,然后只将订单编号更新到driver_info 表的curr_order_id 了,没有将司机信息添加到订单信息中,这时候,系统认为该订单没有派出去,然后又继续通知司机们抢单,会造成一个订单被多个司机师傅接到。相反只将司机信息更新到了订单表中,然后司机表中的curr_order_id没有订单号,这时候司机师傅就不知道自己接到了订单,还会去抢别的订单,造成一个司机同时多个订单。

1.2 场景代码模拟

这里我们就用代码模拟下1.1 节介绍的内容,这里我们使用单体架构多数据源的形式,springboot+mybatis+druid

1.2.1 数据库

这里为了模拟多数据源,我们在同一个实例上面创建了两个db,一个是dirver_db 放的是司机有关的,一个是order_db放的是订单有关的。
在这里插入图片描述
我们需要在这两个库中分别创建driver_info 与order_info 表
司机表:

CREATE TABLE `driver_info` (
  `id_` int(11) NOT NULL AUTO_INCREMENT COMMENT '司机id',
  `driver_name` varchar(20) DEFAULT NULL COMMENT '司机姓名',
  `curr_order_id` int(11) DEFAULT NULL COMMENT '当前接到的订单',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  PRIMARY KEY (`id_`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

插入编号1的司机:

insert into driver_info (id_,driver_name,create_time) values(1,'driver01','2020-07-13 00:00:00')

订单表:

CREATE TABLE `order_info` (
  `id_` int(11) NOT NULL AUTO_INCREMENT COMMENT '订单id\n',
  `driver_id` int(11) DEFAULT NULL COMMENT '接单司机id',
  `create_time` datetime DEFAULT NULL COMMENT '订单创建时间',
  PRIMARY KEY (`id_`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

创建订单号为1的订单:

insert into order_info (id_,create_time) values(1,'2020-07-13 23:00:00')
1.2.2 单体多数据源项目
1.2.2.1 pom
 <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.1.RELEASE</version>
        <relativePath/>
 </parent>
 <dependencies>
		<!--web starter-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
        </dependency>
        <!--druid 数据源-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.21</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
        </dependency>
        <!--mybatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.1.1</version>
        </dependency>
		<!--mysql 	驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>6.0.6</version>
        </dependency>
		<!--单元测试-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
1.2.2.2 配置文件application

我们需要在配置文件application.yml中添加多数据源

# 配置多数据源
spring:
  datasource:
    driver:
      type: com.alibaba.druid.pool.DruidDataSource
      driver-class-name: com.mysql.cj.jdbc.Driver
      jdbc-url: jdbc:mysql://192.168.3.36:3306/driver_db?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true
      username: root
      password: 123456
    order:
      type: com.alibaba.druid.pool.DruidDataSource
      driver-class-name: com.mysql.cj.jdbc.Driver
      jdbc-url: jdbc:mysql://192.168.3.36:3306/order_db?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true
      username: root
      password: 123456
  application:
    name: multi-datasource-distributed-transaction

server:
  port: 8080
1.2.2.3 主启动类

这里就是普通的springboot主启动类
在这里插入图片描述

1.2.2.4 多数据源配置类
  1. DriverDatasourceConfiguration

@Configuration
@MapperScan(basePackages = {"com.xuzhaocai.multi.dao.driver"}, sqlSessionTemplateRef = "driverSqlSessionTemplate")
public class DriverDatasourceConfiguration {
    @Bean(name = "driverDatasource")
    @ConfigurationProperties(prefix = "spring.datasource.driver")
    public DataSource driverDruidDatasource() {

        return DataSourceBuilder.create().build();
        //return xaDataSource;
    }

    //配置数据源
    @Bean(name = "driverSqlSessionFactory")
    public SqlSessionFactory driverSqlSessionFactory(@Qualifier("driverDatasource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver = new PathMatchingResourcePatternResolver();
             //mapper.xml 的位置
        Resource[] resources1 = pathMatchingResourcePatternResolver.getResources("classpath*:com/xuzhaocai/multi/dao/driver/*.xml");
        factoryBean.setMapperLocations(resources1);
        factoryBean.setDataSource(dataSource);
        return factoryBean.getObject();

    }
    // 事务管理器
    @Bean(name = "driverTransactionManger")
    public DataSourceTransactionManager driverTransactionManger(@Qualifier("driverDatasource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean(name = "driverSqlSessionTemplate")
    public SqlSessionTemplate driverSqlSessionTemplate(@Qualifier("driverSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

}
  1. OrderDatasourceConfiguration

@Configuration
@MapperScan(basePackages = {"com.xuzhaocai.multi.dao.order"}, sqlSessionTemplateRef = "orderSqlSessionTemplate")
public class OrderDatasourceConfiguration {



    @Bean(name = "orderDatasource")
    @ConfigurationProperties(prefix = "spring.datasource.order")
    @Primary
    public DataSource orderDatasource() {
        return DataSourceBuilder.create().build();
    }


    //配置数据源
    @Bean(name = "orderSqlSessionFactory")
    @Primary
    public SqlSessionFactory orderSqlSessionFactory(@Qualifier("orderDatasource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver = new PathMatchingResourcePatternResolver();
        Resource[] resources1 = pathMatchingResourcePatternResolver.getResources("classpath*:com/xuzhaocai/multi/dao/order/*.xml");
        factoryBean.setMapperLocations(resources1);
        factoryBean.setDataSource(dataSource);
        return factoryBean.getObject();

    }
    // 事务管理器
    @Bean(name = "orderTransactionManger")
    @Primary
    public DataSourceTransactionManager orderTransactionManger(@Qualifier("orderDatasource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean(name = "orderSqlSessionTemplate")
    @Primary
    public SqlSessionTemplate orderSqlSessionTemplate(@Qualifier("orderSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

}
1.2.2.5 dao 与mapper

这种多数据源是使用分包来区别的,不同的包下的dao用不同的数据源
在这里插入图片描述
DriverInfoDao:

@Repository
public interface DriverInfoDao {
    /**
     * 修改司机信息表中,当前接单的订单
     * @param driverInfo 司机信息
     * @return
     */
    public int updateReceivedOrder(@Param("driverInfo") DriverInfo driverInfo);
    /**
     * 通过司机id获取司机信息
     * @param id  司机id
     * @return 司机信息
     */
    public DriverInfo selectById(@Param("dirverId") Integer id);
}

OrderInfoDao:

@Repository
public interface OrderInfoDao {
    /**
     * 修改订单信息中司机接单信息
     * @param orderInfo 订单详情
     * @return
     */
    public int updateOrderAddDriverInfo(@Param("orderInfo") OrderInfo orderInfo);

    //通过订单ID查询订单信息
    public  OrderInfo selectById(@Param("orderId") Integer oid);
}

同样mapper也是一样的,需要与dao放在同一个目录下:
在这里插入图片描述
DriverInfoMapper.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.xuzhaocai.multi.dao.driver.DriverInfoDao" >
    <update id="updateReceivedOrder">
        update driver_info set curr_order_id=#{driverInfo.currOrderId}  where id_=#{driverInfo.id}
    </update>
    <select id="selectById" resultType="com.xuzhaocai.multi.entity.DriverInfo">
         select
          id_ as id,
          driver_name  as driverName,
          curr_order_id as currOrderId ,
          create_time as  createTime
        from driver_info where id_=#{dirverId}
    </select>
</mapper>

OrderInfoMapper.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.xuzhaocai.multi.dao.order.OrderInfoDao" >
    <update id="updateOrderAddDriverInfo" >
        update order_info  set driver_id = #{orderInfo.driverId}  where id_=#{orderInfo.id}
    </update>
    <select id="selectById" resultType="com.xuzhaocai.multi.entity.OrderInfo">
        select
        id_ as id,
        driver_id as driverId,
        create_time as createTime
        from order_info where id_=#{orderId}
    </select>
</mapper>
1.2.2.5 service与impl

在这里插入图片描述
service
DirverInfoService:

public interface DirverInfoService {
    public String takeOrder(Integer driverId, Integer orderId);
}

OrderInfoService:

public interface OrderInfoService {
}

impl
DriverInfoServiceImpl:

这里我们就根据上面的业务模拟一个司机接单的动作,先是将 司机信息与订单信息拿出来,然后判断司机信息中的当前订单id是否是空,订单信息中的接单司机是否为空,这里还有司机强单问题我们就处理了,我们就当这个司机拿到这个抢单机会,接着就是往订单表中添加司机id,往司机信息中添加订单id,分别更新到库中,当然我们还模拟了出问题。

@Service
public class DriverInfoServiceImpl implements DirverInfoService {
    @Autowired
    private DriverInfoDao driverInfoDao;
    @Autowired
    private OrderInfoDao orderInfoDao;

    //接单操作。
    @Transactional(transactionManager = "driverTransactionManger")
    @Override
    public String takeOrder(Integer driverId, Integer orderId) {
        OrderInfo orderInfo = orderInfoDao.selectById(orderId);
        DriverInfo driverInfo = driverInfoDao.selectById(driverId);
        // 验证 当前司机没有接单,当前订单没有被司机接走
        if (driverInfo==null || driverInfo.getCurrOrderId()!=null || orderInfo==null || orderInfo.getDriverId()!=null  ){
            return "失败";
        }
        orderInfo.setDriverId(driverId);
        driverInfo.setCurrOrderId(orderId);
        int updateDriverFlag = driverInfoDao.updateReceivedOrder(driverInfo);

        int updateOrderFlag = orderInfoDao.updateOrderAddDriverInfo(orderInfo);
        // 模拟出问题
        int i= 1/0;
        if (updateDriverFlag+updateOrderFlag==2){
            return "成功";
        }
        return "失败";
    }
}

OrderInfoServiceImpl:
这个order的service我们这里没用用到

@Service
public class OrderInfoServiceImpl implements OrderInfoService {
}
1.2.2.6 实体

司机实体(driver_info):

// 司机信息
public class DriverInfo {
    private Integer  id;
    private  String driverName;
    private Integer currOrderId;
    private Date createTime;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getDriverName() {
        return driverName;
    }
    public void setDriverName(String driverName) {
        this.driverName = driverName;
    }
    public Integer getCurrOrderId() {
        return currOrderId;
    }
    public void setCurrOrderId(Integer currOrderId) {
        this.currOrderId = currOrderId;
    }
    public Date getCreateTime() {
        return createTime;
    }
    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }
}

订单实体(order_info ):

// 订单信息表
public class OrderInfo {
    private Integer id;
    private Integer driverId;
    private Date createTime;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public Integer getDriverId() {
        return driverId;
    }
    public void setDriverId(Integer driverId) {
        this.driverId = driverId;
    }
    public Date getCreateTime() {
        return createTime;
    }
    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }
}

1.2.2.7 controller

这里我们使用controller来触发这个接单操作

@RestController
@RequestMapping("/driver/")
public class DriverInfoController {
    @Autowired
    private DirverInfoService dirverInfoService;
    /**
     * 接单方法
     * @return
     */
    @RequestMapping(value = "/takeOrder",method = RequestMethod.GET)
    public String takeOrder(){
        Integer driverId =1;
        Integer orderId=1;
        return dirverInfoService.takeOrder(driverId,orderId);
    }
}
1.2.2.8 触发接单,查看效果

我们启动项目,然后请求接口http://127.0.0.1:8080/driver/takeOrder 触发接单。
请求完成后我们可以看到这里出了问题,然后我们看下数据库里面的表:
在driver_info司机表中没有插入订单id,也就是司机端就看不到自己接单了
在这里插入图片描述
而在订单表中,则是有了司机id。
在这里插入图片描述
这时,可以发现数据出现了不一致的情况,司机没有接到订单,而订单中确是这个司机接到到这个订单。
我们可以看下代码,代码中我们使用是事务管理器是司机那个数据源的,然后报错后,会把司机这个操作回滚。而订单操作的这个事务它是管不到的,所以就没有回滚。
在这里插入图片描述
那么我们使用订单的事务管理器可以不,答案是不可以,订单管理器也只能管着订单的回滚,并不能管着司机操作的回滚,同样会出现数据不一致的情况。

2.使用jta+atomikos解决多数据源事务方案

2.1 jta是什么

百度百科是这样说的

JTA,即Java Transaction API,JTA允许应用程序执行分布式事务处理——在两个或多个网络计算机资源上访问并且更新数据。JDBC驱动程序的JTA支持极大地增强了数据访问能力。
JTA与JDBC对比:
JTA事务比JDBC事务更强大。一个JTA事务可以有多个参与者,而一个JDBC事务则被限定在一个单一的数据库连接。下列任一个Java平台的组件都可以参与到一个JTA事务中:JDBC连接、JDO PersistenceManager 对象、JMS 队列、JMS 主题、企业JavaBeans(EJB)、一个用J2EE Connector Architecture 规范编译的资源分配器。

2.2 atomikos是什么

atomikos是一个实现JTA事务管理第三方管理工具,为Java平台提供增值服务的并且开源类事务管理器。

2.3 改造上述案例实现多数据源事务管理
2.3.1 pom

pom文件中添加对atomikos的支持

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>
2.3.2 配置文件application

需要将配置文件中jdbc-url改成url

spring:
  datasource:
    driver:
      type: com.alibaba.druid.pool.DruidDataSource
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://192.168.3.36:3306/driver_db?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true
      username: root
      password: 123456
    order:
      type: com.alibaba.druid.pool.DruidDataSource
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://192.168.3.36:3306/order_db?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true
      username: root
      password: 123456
  application:
    name: multi-datasource-distributed-transaction

server:
  port: 8080
2.3.3 编写数据源配置properties

这里只是将配置文件中的配置信息放到properties中
DriverDatasourceProperties:

@Component
@ConfigurationProperties(prefix = "spring.datasource.driver")
public class DriverDatasourceProperties {
    private String driverClassName;
    private String url;
    private String username;
    private String password;
    public String getDriverClassName() {
        return driverClassName;
    }
    public void setDriverClassName(String driverClassName) {
        this.driverClassName = driverClassName;
    }
    public String getUrl() {
        return url;
    }
    public void setUrl(String url) {
        this.url = url;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}

OrderDatasourceProperties:

@Component
@ConfigurationProperties(prefix = "spring.datasource.order")
public class OrderDatasourceProperties {
    private String driverClassName;
    private String url;
    private String username;
    private String password;
    public String getDriverClassName() {
        return driverClassName;
    }
    public void setDriverClassName(String driverClassName) {
        this.driverClassName = driverClassName;
    }
    public String getUrl() {
        return url;
    }
    public void setUrl(String url) {
        this.url = url;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}
2.3.4 修改数据源配置

这里主要是将datasource包上一层atomikos的datasource。
DriverDatasourceConfiguration:

@Configuration
@MapperScan(basePackages = {"com.xuzhaocai.multi.dao.driver"}, sqlSessionTemplateRef = "driverSqlSessionTemplate")
public class DriverDatasourceConfiguration {
    @Autowired
    private DriverDatasourceProperties driverDatasourceProperties;
    @Bean(name = "driverDatasource")
    public DataSource driverDruidDatasource() {
        DruidXADataSource datasource = new DruidXADataSource();
        BeanUtils.copyProperties(driverDatasourceProperties,datasource);
        AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
        xaDataSource.setXaDataSource(datasource);
        xaDataSource.setUniqueResourceName("driverDatasource");
        //return DataSourceBuilder.create().build();
        return xaDataSource;
    }

    //配置数据源
    @Bean(name = "driverSqlSessionFactory")
    public SqlSessionFactory driverSqlSessionFactory(@Qualifier("driverDatasource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver = new PathMatchingResourcePatternResolver();
             //mapper.xml 的位置
        Resource[] resources1 = pathMatchingResourcePatternResolver.getResources("classpath*:com/xuzhaocai/multi/dao/driver/*.xml");
        factoryBean.setMapperLocations(resources1);
        factoryBean.setDataSource(dataSource);
        return factoryBean.getObject();
    }
    // 事务管理器
   /* @Bean(name = "driverTransactionManger")
    public DataSourceTransactionManager driverTransactionManger(@Qualifier("driverDatasource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }*/

    @Bean(name = "driverSqlSessionTemplate")
    public SqlSessionTemplate driverSqlSessionTemplate(@Qualifier("driverSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

OrderDatasourceConfiguration:

@Configuration
@MapperScan(basePackages = {"com.xuzhaocai.multi.dao.order"}, sqlSessionTemplateRef = "orderSqlSessionTemplate")
public class OrderDatasourceConfiguration {

    @Autowired
    private  OrderDatasourceProperties orderDatasourceProperties;
    @Bean(name = "orderDatasource")
    @Primary
    public DataSource orderDatasource() {
        DruidXADataSource datasource = new DruidXADataSource();
        BeanUtils.copyProperties(orderDatasourceProperties,datasource);
        AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
        xaDataSource.setXaDataSource(datasource);
        xaDataSource.setUniqueResourceName("orderDatasource");
        return xaDataSource;
    }
    //配置数据源
    @Bean(name = "orderSqlSessionFactory")
    @Primary
    public SqlSessionFactory orderSqlSessionFactory(@Qualifier("orderDatasource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver = new PathMatchingResourcePatternResolver();
        Resource[] resources1 = pathMatchingResourcePatternResolver.getResources("classpath*:com/xuzhaocai/multi/dao/order/*.xml");
        factoryBean.setMapperLocations(resources1);
        factoryBean.setDataSource(dataSource);
        return factoryBean.getObject();

    }
   /* // 事务管理器
    @Bean(name = "orderTransactionManger")
    @Primary
    public DataSourceTransactionManager orderTransactionManger(@Qualifier("orderDatasource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }*/

    @Bean(name = "orderSqlSessionTemplate")
    @Primary
    public SqlSessionTemplate orderSqlSessionTemplate(@Qualifier("orderSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

}
2.3.5 修改接单方法

这里就不需要指定事务管理器了,直接使用Transactional注解就好了。

	@Transactional()
    @Override
    public String takeOrder(Integer driverId, Integer orderId) {
        OrderInfo orderInfo = orderInfoDao.selectById(orderId);
        DriverInfo driverInfo = driverInfoDao.selectById(driverId);
        // 验证 当前司机没有接单,当前订单没有被司机接走
        if (driverInfo==null || driverInfo.getCurrOrderId()!=null || orderInfo==null || orderInfo.getDriverId()!=null  ){
            return "失败";
        }
        orderInfo.setDriverId(driverId);
        driverInfo.setCurrOrderId(orderId);
        int updateDriverFlag = driverInfoDao.updateReceivedOrder(driverInfo);
        int updateOrderFlag = orderInfoDao.updateOrderAddDriverInfo(orderInfo);
        // 模拟出问题
        int i= 1/0;
        if (updateDriverFlag+updateOrderFlag==2){
            return "成功";
        }
        return "失败";
    }
2.3.6 测试

首先我们要把数据置为原始样子,然后重启项目,发起接单请求,这时候再看数据情况,发现报错之后都没有提交事务,这就达成了数据的一致性。
在这里插入图片描述
在这里插入图片描述

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

$码出未来

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值