事务—【02】SpringBoot使用JTA Atomikos实现多数据源事务管理

0. 前置知识

0.1 事务的一些基本概念

0.2 X/OPEN DTP模型

  • 全称:X/Open Distributed Transaction Processing Reference Model, 也就是一个分布式事务处理的模型
  • DTP定义的三个组成部分,分别是:AP(Application Program,应用程序)、RM(Resource Manager,资源管理器)、TM(Transaction Manager,事务管理器)
  1. AP:就是使用DTP模型的程序
  2. RM:资源管理器(可以理解为数据库系统、消息队列系统)
  3. TM:事务管理器

0.3 X/OPEN XA规范

  • wiki:https://zh.wikipedia.org/wiki/X/Open_XA
  • XA规范就是提供一套通用的调用接口规范让TM可以调用不同的RM,也是让不同RM根据XA规范来实现自己的RM。
  • XA使用两阶段提交(2PC)来保证所有资源同时提交或回滚任何特定的事务

MySQL的InnoDB存储引擎支持XA规范。

0.4 2PC 两阶段提交

  • wiki: https://zh.wikipedia.org/wiki/%E4%BA%8C%E9%98%B6%E6%AE%B5%E6%8F%90%E4%BA%A4
  • 两阶段提交,顾名思义把事务的执行分成了两个阶段,第一个阶段就是提交请求或者说投票阶段,事务管理器把要执行的任务告诉资源管理器,并问它可不可以做?多个资源管理器分别回复给OK和不OK; 第二个阶段就是执行阶段:事务管理器收到各个资源管理器的回复,如果全是OK那就干!如果有不OK的那就不干了。
  • 详细的可以看下wiki 很详细

0.5 JTA Java事务API

  • 全称是:Java Transaction API,是JSR 907规范提出的 允许完成跨越多个XA资源的分布式事务编程接口,它包括用户操作接口,JTATransactionManager,XAResource.
  • 它是XA规范在Java中的实现。

  • 我们在接下来的实例中就会使用JTA事务管理器来管理两个数据库操作的事务。

1. 多数据源事务管理

1.1 环境说明

1.1.1 组件说明

1.1.2 项目关键依赖

    <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.1</version>
        </dependency>
        <!--atomikos transaction management-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jta-atomikos</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.21</version>
        </dependency>

1.1.3 多数据源

  • 数据源使用两个数据库的不同表
  • 都是用Druid做连接池,然后用Atomikos管理

1.1.4 JTA的工具

  • SpringBoot可以用的官方说了两个一个是Atomikos,另一个是Bitronix,除此之外还可以在支持JTA的web server中用。(Tomcat不支持)
  • SpringBoot文档中的说明:当检测到JTA环境时,将使用Spring的JtaTransactionManager来管理事务。JMSDataSourceJPA已升级为支持XA事务。可以用标准的Spring用法(例如@Transactional)来参与分布式事务。如果您在JTA环境中,并且仍要使用本地事务,则可以将spring.jta.enabled属性设置为false以禁用JTA自动配置。

1.2 实例业务说明

  • 简单逻辑,两张表,分别在两个不同的库中,然后一个service方法操作两个库的数据。

1.3 多数据源配置

1.3.1 配置文件

  • 第一张表:是账户表
  • 第二章表:是订单表
spring:
  application:
    name: two-data-source
  datasource:
    account:
      url: jdbc:mysql://127.0.0.1:3306/transaction_account?useSSL=false&characterEncoding=UTF-8
      username: root
      password: xxxxx
    order:
      url: jdbc:mysql://127.0.0.1:3306/transaction_order?useSSL=false&characterEncoding=UTF-8
      username: root
      password: xxxxx
#logging:
#  level:
#    root: DEBUG

1.3.2 Bean注册

  • 主要包括以下步骤
  1. 分别注册对应DataSource、SqlSessionFactory、SqlSessionTemplate的Bean
  2. 然后指定表的Mapper的位置,并且把template设置成你注册的。
  • order库的类似 此处略

  • 需要注意的点:
  1. DataSource不能直接使用Druid提供的DruidDataSource, 需要使用atomikos来包装一下Druid提供的DruidXADataSource,来支持XA规范
  2. 如果你不想用Druid,可以考虑使用MysqlXADataSource(我没试过)
  3. 注册的Bean的对应关系要正确
@Configuration
@MapperScan(basePackages = {"io.ilss.transaction.twodatasource.dao.account"}, sqlSessionTemplateRef = "accountSqlSessionTemplate")
public class AccountConfiguration {

    @Value("${spring.datasource.account.url}")
    private String url;
    @Value("${spring.datasource.account.username}")
    private String username;
    @Value("${spring.datasource.account.password}")
    private String password;



    @Bean(name = "accountDataSource")
    public DataSource accountDataSource() {
        AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();
        DruidXADataSource druidXADataSource = new DruidXADataSource();
        druidXADataSource.setUrl(url);
        druidXADataSource.setUsername(username);
        druidXADataSource.setPassword(password);
        druidXADataSource.setName("druidDataSource-account");
        atomikosDataSourceBean.setXaDataSource(druidXADataSource);
        atomikosDataSourceBean.setUniqueResourceName("accountResource");
        return atomikosDataSourceBean;
    }

    @Bean(name = "accountSqlSessionFactory")
    public SqlSessionFactory accountSqlSessionFactory(DataSource accountDataSource) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(accountDataSource);
        factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mappers/account/*.xml"));
        return factoryBean.getObject();
    }

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


  • 配置正确后会有如下日志信息
c.atomikos.jdbc.AbstractDataSourceBean   : AtomikosDataSoureBean 'orderResource': poolSize equals default - this may cause performance problems!
com.alibaba.druid.pool.DruidDataSource   : {dataSource-1,druidDataSource-order} inited
c.atomikos.jdbc.AbstractDataSourceBean   : AtomikosDataSoureBean 'accountResource': poolSize equals default - this may cause performance problems!
com.alibaba.druid.pool.DruidDataSource   : {dataSource-2,druidDataSource-account} inited
c.a.icatch.provider.imp.AssemblerImp     : Loaded jar:file:/Users/feng/.m2/repository/com/atomikos/transactions/4.0.6/transactions-4.0.6.jar!/transactions-defaults.properties
c.a.icatch.provider.imp.AssemblerImp     : Thanks for using Atomikos! Evaluate http://www.atomikos.com/Main/ExtremeTransactions for advanced features and professional support...略
c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.default_max_wait_time_on_shutdown = 9223372036854775807
c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.allow_subtransactions = true
c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.recovery_delay = 10000
c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.automatic_resource_registration = true
c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.oltp_max_retries = 5
c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.client_demarcation = false
c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.threaded_2pc = false
c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.serial_jta_transactions = true
c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.log_base_dir = /Users/feng/Projects/java/transaction-example/transaction-logs
c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.rmi_export_class = none
c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.max_actives = 50
c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.checkpoint_interval = 500
c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.enable_logging = true
c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.log_base_name = tmlog
c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.max_timeout = 300000
c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.trust_client_tm = false
c.a.icatch.provider.imp.AssemblerImp     : USING: java.naming.factory.initial = com.sun.jndi.rmi.registry.RegistryContextFactory
c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.tm_unique_name = 10.11.11.11.tm
c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.forget_orphaned_log_entries_delay = 86400000
c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.oltp_retry_interval = 10000
c.a.icatch.provider.imp.AssemblerImp     : USING: java.naming.provider.url = rmi://localhost:1099
c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.force_shutdown_on_vm_exit = false
c.a.icatch.provider.imp.AssemblerImp     : USING: com.atomikos.icatch.default_jta_timeout = 10000
c.a.icatch.provider.imp.AssemblerImp     : Using default (local) logging and recovery...
c.a.d.xa.XATransactionalResource         : orderResource: refreshed XAResource
c.a.d.xa.XATransactionalResource         : accountResource: refreshed XAResource
  • 首先初始化两个Atomikos包裹的Druid的数据源,

  • 然后设置atomikos的参数,都是默认的

  • 最后XAResource刷新

  • 至此,配置完毕,可能有人好奇,JTA的代码一个都没有,因为SpringBoot使用JTA的时候引入的starter做了

1.4 事务实例

  • 简单模拟订单生成支付过程,从账户中扣除一比钱,然后新增一比订单。
  • 编程的方式和Spring事务的方式一毛一样,没什么不同。

1.4.1 实现代码

@Slf4j
@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private OrderInfoDAO orderInfoDAO;

    @Autowired
    private AccountDAO accountDAO;

    @Autowired
    PlatformTransactionManager transactionManager;

    @Override
    @Transactional
    public String createOrder(OrderInfoDO orderInfoDO) {
        AccountDO accountDO = accountDAO.selectByPrimaryKey(orderInfoDO.getAccountId());
        if (null == accountDO) {
            log.error("createOrder user is not present, accountId: {}", orderInfoDO.getAccountId());
            return "用户不存在!";
        }
        // 用户费用扣除
        accountDO.setBalance(accountDO.getBalance().subtract(orderInfoDO.getAmount()));
        accountDAO.updateByPrimaryKey(accountDO);
        orderInfoDAO.insertSelective(orderInfoDO);

        return "成功";
    }

    @Override
    public String createOrderCode(OrderInfoDO orderInfoDO) {
        TransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
        // 获取事务 开始业务执行
        TransactionStatus transaction = transactionManager.getTransaction(transactionDefinition);
        try {
            AccountDO accountDO = accountDAO.selectByPrimaryKey(orderInfoDO.getAccountId());
            if (null == accountDO) {
                log.error("createOrder user is not present, accountId: {}", orderInfoDO.getAccountId());
                return "用户不存在!";
            }
            // 用户费用扣除
            accountDO.setBalance(accountDO.getBalance().subtract(orderInfoDO.getAmount()));
            accountDAO.updateByPrimaryKey(accountDO);
            orderInfoDAO.insertSelective(orderInfoDO);
            error("createOrderCode error");
            transactionManager.commit(transaction);

            return "成功";
        } catch (Exception e) {
            log.error("create order failed, accountId: {}, errMsg: {}", orderInfoDO.getAccountId(), e.getMessage());
            transactionManager.rollback(transaction);
        }

        return "失败";
    }

    public static void error(String  msg) {
        throw new RuntimeException(msg);
    }
}

1.4.2 处理过程

  • 日志参数设置成:logging.level.root=DEBUG 如果不设置,你基本看不到什么事务日志。

o.s.web.servlet.DispatcherServlet        : GET "/api/order/create", parameters={}
s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to io.ilss.transaction.twodatasource.web.OrderController#create()
o.s.t.jta.JtaTransactionManager          : Creating new transaction with name [io.ilss.transaction.twodatasource.service.impl.OrderServiceImpl.createOrder]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
c.a.i.i.CompositeTransactionManagerImp   : createCompositeTransaction ( 10000 ): created new ROOT transaction with id 10.11.11.11.tm157866358813800001
org.mybatis.spring.SqlSessionUtils       : Creating a new SqlSession
org.mybatis.spring.SqlSessionUtils       : Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1502ae81]
o.s.jdbc.datasource.DataSourceUtils      : Fetching JDBC Connection from DataSource
c.atomikos.jdbc.AbstractDataSourceBean   : AtomikosDataSoureBean 'accountResource': getConnection()...
c.atomikos.jdbc.AbstractDataSourceBean   : AtomikosDataSoureBean 'accountResource': init...
c.atomikos.jdbc.AtomikosConnectionProxy  : atomikos connection proxy for com.mysql.jdbc.JDBC4Connection@73c7f762: calling getAutoCommit...
c.atomikos.jdbc.AtomikosConnectionProxy  : atomikos connection proxy for com.mysql.jdbc.JDBC4Connection@73c7f762: calling toString...
o.m.s.t.SpringManagedTransaction         : JDBC Connection [com.mysql.jdbc.JDBC4Connection@73c7f762] will be managed by Spring
i.i.t.t.d.a.A.selectByPrimaryKey         : ==>  Preparing: select id, nickname, username, `password`, balance, create_time, update_time from account where id = ?
c.a.icatch.imp.CompositeTransactionImp   : addParticipant ( XAResourceTransaction: 31302E31312E31312E31312E746D313537383636333538383133383030303031:31302E31312E31312E31312E746D31 ) for transaction 10.11.11.11.tm157866358813800001
c.a.datasource.xa.XAResourceTransaction  : XAResource.start ( 31302E31312E31312E31312E746D313537383636333538383133383030303031:31302E31312E31312E31312E746D31 , XAResource.TMNOFLAGS ) on resource accountResource represented by XAResource instance com.mysql.jdbc.jdbc2.optional.MysqlXAConnection@51af9ae0
c.a.icatch.imp.CompositeTransactionImp   : registerSynchronization ( com.atomikos.jdbc.AtomikosConnectionProxy$JdbcRequeueSynchronization@853ac8fa ) for transaction 10.11.11.11.tm157866358813800001
c.atomikos.jdbc.AtomikosConnectionProxy  : atomikos connection proxy for com.mysql.jdbc.JDBC4Connection@73c7f762: calling prepareStatement(select....略
i.i.t.t.d.a.A.selectByPrimaryKey         : ==> Parameters: 1(Long)
i.i.t.t.d.a.A.selectByPrimaryKey         : <==      Total: 1
org.mybatis.spring.SqlSessionUtils       : Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1502ae81]
org.mybatis.spring.SqlSessionUtils       : Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1502ae81] from current transaction
i.i.t.t.d.a.A.updateByPrimaryKey         : ==>  Preparing: update account set nickname = ?, username = ?, `password` = ?, balance = ?, create_time = ?, update_time = ? where id = ?
c.a.icatch.imp.CompositeTransactionImp   : registerSynchronization ( com.atomikos.jdbc.AtomikosConnectionProxy$JdbcRequeueSynchronization@853ac8fa ) for transaction 10.11.11.11.tm157866358813800001
c.atomikos.jdbc.AtomikosConnectionProxy  : atomikos connection proxy for com.mysql.jdbc.JDBC4Connection@73c7f762: calling prepareStatement(update account
i.i.t.t.d.a.A.updateByPrimaryKey         : ==> Parameters: 小一(String), xiaoyi(String), 123456(String), 600.00(BigDecimal), 2020-01-09T17:04:28(LocalDateTime), 2020-01-10T16:00:51(LocalDateTime), 1(Long)
i.i.t.t.d.a.A.updateByPrimaryKey         : <==    Updates: 1
org.mybatis.spring.SqlSessionUtils       : Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1502ae81]
org.mybatis.spring.SqlSessionUtils       : Creating a new SqlSession
org.mybatis.spring.SqlSessionUtils       : Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@20a0ef33]
o.s.jdbc.datasource.DataSourceUtils      : Fetching JDBC Connection from DataSource
c.atomikos.jdbc.AbstractDataSourceBean   : AtomikosDataSoureBean 'orderResource': getConnection()...
c.atomikos.jdbc.AbstractDataSourceBean   : AtomikosDataSoureBean 'orderResource': init...
c.atomikos.jdbc.AtomikosConnectionProxy  : atomikos connection proxy for com.mysql.jdbc.JDBC4Connection@22a176f4: calling getAutoCommit...
c.atomikos.jdbc.AtomikosConnectionProxy  : atomikos connection proxy for com.mysql.jdbc.JDBC4Connection@22a176f4: calling toString...
o.m.s.t.SpringManagedTransaction         : JDBC Connection [com.mysql.jdbc.JDBC4Connection@22a176f4] will be managed by Spring
i.i.t.t.d.o.O.insertSelective            : ==>  Preparing: insert into order_info ( account_id, completed, order_state, detail, amount, create_time, update_time ) values ( ?, ?, ?, ?, ?, ?, ? )
c.a.icatch.imp.CompositeTransactionImp   : addParticipant ( XAResourceTransaction: 31302E31312E31312E31312E746D313537383636333538383133383030303031:31302E31312E31312E31312E746D32 ) for transaction 10.11.11.11.tm157866358813800001
c.a.datasource.xa.XAResourceTransaction  : XAResource.start ( 31302E31312E31312E31312E746D313537383636333538383133383030303031:31302E31312E31312E31312E746D32 , XAResource.TMNOFLAGS ) on resource orderResource represented by XAResource instance com.mysql.jdbc.jdbc2.optional.MysqlXAConnection@473dd4ea
c.a.icatch.imp.CompositeTransactionImp   : registerSynchronization ( com.atomikos.jdbc.AtomikosConnectionProxy$JdbcRequeueSynchronization@853ac8fa ) for transaction 10.11.11.11.tm157866358813800001
c.atomikos.jdbc.AtomikosConnectionProxy  : atomikos connection proxy for com.mysql.jdbc.JDBC4Connection@22a176f4: calling prepareStatement(insert into order_info
i.i.t.t.d.o.O.insertSelective            : ==> Parameters: 1(Long), 0(Integer), 1(Integer), 买衣服(String), 100(BigDecimal), 2020-01-10T21:39:48.134(LocalDateTime), 2020-01-10T21:39:48.134(LocalDateTime)
i.i.t.t.d.o.O.insertSelective            : <==    Updates: 1
org.mybatis.spring.SqlSessionUtils       : Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@20a0ef33]
org.mybatis.spring.SqlSessionUtils       : Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1502ae81]
org.mybatis.spring.SqlSessionUtils       : Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@20a0ef33]
org.mybatis.spring.SqlSessionUtils       : Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1502ae81]
org.mybatis.spring.SqlSessionUtils       : Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1502ae81]
org.mybatis.spring.SqlSessionUtils       : Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@20a0ef33]
org.mybatis.spring.SqlSessionUtils       : Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@20a0ef33]
c.atomikos.jdbc.AtomikosConnectionProxy  : atomikos connection proxy for com.mysql.jdbc.JDBC4Connection@73c7f762: close()...
c.a.datasource.xa.XAResourceTransaction  : XAResource.end ( 31302E31312E31312E31312E746D313537383636333538383133383030303031:31302E31312E31312E31312E746D31 , XAResource.TMSUCCESS ) on resource accountResource represented by XAResource instance com.mysql.jdbc.jdbc2.optional.MysqlXAConnection@51af9ae0
c.atomikos.jdbc.AtomikosConnectionProxy  : atomikos connection proxy for com.mysql.jdbc.JDBC4Connection@22a176f4: close()...
c.a.datasource.xa.XAResourceTransaction  : XAResource.end ( 31302E31312E31312E31312E746D313537383636333538383133383030303031:31302E31312E31312E31312E746D32 , XAResource.TMSUCCESS ) on resource orderResource represented by XAResource instance com.mysql.jdbc.jdbc2.optional.MysqlXAConnection@473dd4ea
o.s.t.jta.JtaTransactionManager          : Initiating transaction commit
c.a.icatch.imp.CompositeTransactionImp   : commit() done (by application) of transaction 10.11.11.11.tm157866358813800001
c.a.datasource.xa.XAResourceTransaction  : XAResource.prepare ( 31302E31312E31312E31312E746D313537383636333538383133383030303031:31302E31312E31312E31312E746D31 ) returning OK on resource accountResource represented by XAResource instance com.mysql.jdbc.jdbc2.optional.MysqlXAConnection@51af9ae0
c.a.datasource.xa.XAResourceTransaction  : XAResource.prepare ( 31302E31312E31312E31312E746D313537383636333538383133383030303031:31302E31312E31312E31312E746D32 ) returning OK on resource orderResource represented by XAResource instance com.mysql.jdbc.jdbc2.optional.MysqlXAConnection@473dd4ea
c.a.datasource.xa.XAResourceTransaction  : XAResource.commit ( 31302E31312E31312E31312E746D313537383636333538383133383030303031:31302E31312E31312E31312E746D31 , false ) on resource accountResource represented by XAResource instance com.mysql.jdbc.jdbc2.optional.MysqlXAConnection@51af9ae0
c.a.datasource.xa.XAResourceTransaction  : XAResource.commit ( 31302E31312E31312E31312E746D313537383636333538383133383030303031:31302E31312E31312E31312E746D32 , false ) on resource orderResource represented by XAResource instance com.mysql.jdbc.jdbc2.optional.MysqlXAConnection@473dd4ea
m.m.a.RequestResponseBodyMethodProcessor : Using 'text/html', given [text/html, application/xhtml+xml, image/webp, image/apng, application/xml;q=0.9, application/signed-exchange;v=b3;q=0.9, */*;q=0.8] and supported [text/plain, */*, text/plain, */*, application/json, application/*+json, application/json, application/*+json]
m.m.a.RequestResponseBodyMethodProcessor : Writing ["成功"]
o.s.web.servlet.DispatcherServlet        : Completed 200 OK
  • 过程:
  1. 请求进入,经由OrderController调用,JtaTransactionManager创建新事务
  2. Atomikos开始获取account库的连接
  3. 查询没有事务处理,随后的更新,XAResource注册了一个事务并生成了一个XID
  4. 然后处理新增订单,同样的Atomikos注册了一个事务并生成了一个XID
  5. 最后准备,然后提交。
  6. 请求成功 返回200.

o.s.web.servlet.DispatcherServlet        : GET "/api/order/create/code", parameters={}
s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to io.ilss.transaction.twodatasource.web.OrderController#createCode()
o.s.t.jta.JtaTransactionManager          : Creating new transaction with name [null]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
c.a.i.i.CompositeTransactionManagerImp   : createCompositeTransaction ( 10000 ): created new ROOT transaction with id 10.11.11.11.tm157866420875900002
org.mybatis.spring.SqlSessionUtils       : Creating a new SqlSession
org.mybatis.spring.SqlSessionUtils       : Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@48f4fdff]
o.s.jdbc.datasource.DataSourceUtils      : Fetching JDBC Connection from DataSource
c.atomikos.jdbc.AbstractDataSourceBean   : AtomikosDataSoureBean 'accountResource': getConnection()...
c.atomikos.jdbc.AbstractDataSourceBean   : AtomikosDataSoureBean 'accountResource': init...
c.atomikos.jdbc.AtomikosConnectionProxy  : atomikos connection proxy for com.mysql.jdbc.JDBC4Connection@73c7f762: calling getAutoCommit...
c.atomikos.jdbc.AtomikosConnectionProxy  : atomikos connection proxy for com.mysql.jdbc.JDBC4Connection@73c7f762: calling toString...
o.m.s.t.SpringManagedTransaction         : JDBC Connection [com.mysql.jdbc.JDBC4Connection@73c7f762] will be managed by Spring
i.i.t.t.d.a.A.selectByPrimaryKey         : ==>  Preparing: select id, nickname, username, `password`, balance, create_time, update_time from account where id = ?
c.a.icatch.imp.CompositeTransactionImp   : addParticipant ( XAResourceTransaction: 31302E31312E31312E31312E746D313537383636343230383735393030303032:31302E31312E31312E31312E746D33 ) for transaction 10.11.11.11.tm157866420875900002
c.a.datasource.xa.XAResourceTransaction  : XAResource.start ( 31302E31312E31312E31312E746D313537383636343230383735393030303032:31302E31312E31312E31312E746D33 , XAResource.TMNOFLAGS ) on resource accountResource represented by XAResource instance com.mysql.jdbc.jdbc2.optional.MysqlXAConnection@51af9ae0
c.a.icatch.imp.CompositeTransactionImp   : registerSynchronization ( com.atomikos.jdbc.AtomikosConnectionProxy$JdbcRequeueSynchronization@6bb297a ) for transaction 10.11.11.11.tm157866420875900002
c.atomikos.jdbc.AtomikosConnectionProxy  : atomikos connection proxy for com.mysql.jdbc.JDBC4Connection@73c7f762: calling prepareStatement(select....略
i.i.t.t.d.a.A.selectByPrimaryKey         : ==> Parameters: 1(Long)
i.i.t.t.d.a.A.selectByPrimaryKey         : <==      Total: 1
org.mybatis.spring.SqlSessionUtils       : Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@48f4fdff]
org.mybatis.spring.SqlSessionUtils       : Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@48f4fdff] from current transaction
i.i.t.t.d.a.A.updateByPrimaryKey         : ==>  Preparing: update account set nickname = ?, username = ?, `password` = ?, balance = ?, create_time = ?, update_time = ? where id = ?
c.a.icatch.imp.CompositeTransactionImp   : registerSynchronization ( com.atomikos.jdbc.AtomikosConnectionProxy$JdbcRequeueSynchronization@6bb297a ) for transaction 10.11.11.11.tm157866420875900002
c.atomikos.jdbc.AtomikosConnectionProxy  : atomikos connection proxy for com.mysql.jdbc.JDBC4Connection@73c7f762: calling prepareStatement(update account... 略
i.i.t.t.d.a.A.updateByPrimaryKey         : ==> Parameters: 小一(String), xiaoyi(String), 123456(String), 500.00(BigDecimal), 2020-01-09T17:04:28(LocalDateTime), 2020-01-10T16:00:51(LocalDateTime), 1(Long)
i.i.t.t.d.a.A.updateByPrimaryKey         : <==    Updates: 1
org.mybatis.spring.SqlSessionUtils       : Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@48f4fdff]
org.mybatis.spring.SqlSessionUtils       : Creating a new SqlSession
org.mybatis.spring.SqlSessionUtils       : Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1901cd8f]
o.s.jdbc.datasource.DataSourceUtils      : Fetching JDBC Connection from DataSource
c.atomikos.jdbc.AbstractDataSourceBean   : AtomikosDataSoureBean 'orderResource': getConnection()...
c.atomikos.jdbc.AbstractDataSourceBean   : AtomikosDataSoureBean 'orderResource': init...
c.atomikos.jdbc.AtomikosConnectionProxy  : atomikos connection proxy for com.mysql.jdbc.JDBC4Connection@22a176f4: calling getAutoCommit...
c.atomikos.jdbc.AtomikosConnectionProxy  : atomikos connection proxy for com.mysql.jdbc.JDBC4Connection@22a176f4: calling toString...
o.m.s.t.SpringManagedTransaction         : JDBC Connection [com.mysql.jdbc.JDBC4Connection@22a176f4] will be managed by Spring
i.i.t.t.d.o.O.insertSelective            : ==>  Preparing: insert into order_info ( account_id, completed, order_state, detail, amount, create_time, update_time ) values ( ?, ?, ?, ?, ?, ?, ? )
c.a.icatch.imp.CompositeTransactionImp   : addParticipant ( XAResourceTransaction: 31302E31312E31312E31312E746D313537383636343230383735393030303032:31302E31312E31312E31312E746D34 ) for transaction 10.11.11.11.tm157866420875900002
c.a.datasource.xa.XAResourceTransaction  : XAResource.start ( 31302E31312E31312E31312E746D313537383636343230383735393030303032:31302E31312E31312E31312E746D34 , XAResource.TMNOFLAGS ) on resource orderResource represented by XAResource instance com.mysql.jdbc.jdbc2.optional.MysqlXAConnection@473dd4ea
c.a.icatch.imp.CompositeTransactionImp   : registerSynchronization ( com.atomikos.jdbc.AtomikosConnectionProxy$JdbcRequeueSynchronization@6bb297a ) for transaction 10.11.11.11.tm157866420875900002
c.atomikos.jdbc.AtomikosConnectionProxy  : atomikos connection proxy for com.mysql.jdbc.JDBC4Connection@22a176f4: calling prepareStatement(insert into order_info...略
i.i.t.t.d.o.O.insertSelective            : ==> Parameters: 1(Long), 0(Integer), 1(Integer), 买衣服(String), 100(BigDecimal), 2020-01-10T21:50:08.759(LocalDateTime), 2020-01-10T21:50:08.759(LocalDateTime)
i.i.t.t.d.o.O.insertSelective            : <==    Updates: 1
org.mybatis.spring.SqlSessionUtils       : Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1901cd8f]
i.i.t.t.service.impl.OrderServiceImpl    : create order failed, accountId: 1, errMsg: createOrderCode error
org.mybatis.spring.SqlSessionUtils       : Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@48f4fdff]
org.mybatis.spring.SqlSessionUtils       : Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@48f4fdff]
org.mybatis.spring.SqlSessionUtils       : Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1901cd8f]
org.mybatis.spring.SqlSessionUtils       : Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1901cd8f]
c.atomikos.jdbc.AtomikosConnectionProxy  : atomikos connection proxy for com.mysql.jdbc.JDBC4Connection@73c7f762: close()...
c.a.datasource.xa.XAResourceTransaction  : XAResource.end ( 31302E31312E31312E31312E746D313537383636343230383735393030303032:31302E31312E31312E31312E746D33 , XAResource.TMSUCCESS ) on resource accountResource represented by XAResource instance com.mysql.jdbc.jdbc2.optional.MysqlXAConnection@51af9ae0
c.atomikos.jdbc.AtomikosConnectionProxy  : atomikos connection proxy for com.mysql.jdbc.JDBC4Connection@22a176f4: close()...
c.a.datasource.xa.XAResourceTransaction  : XAResource.end ( 31302E31312E31312E31312E746D313537383636343230383735393030303032:31302E31312E31312E31312E746D34 , XAResource.TMSUCCESS ) on resource orderResource represented by XAResource instance com.mysql.jdbc.jdbc2.optional.MysqlXAConnection@473dd4ea
o.s.t.jta.JtaTransactionManager          : Initiating transaction rollback
c.a.datasource.xa.XAResourceTransaction  : XAResource.rollback ( 31302E31312E31312E31312E746D313537383636343230383735393030303032:31302E31312E31312E31312E746D33 ) on resource accountResource represented by XAResource instance com.mysql.jdbc.jdbc2.optional.MysqlXAConnection@51af9ae0
c.a.datasource.xa.XAResourceTransaction  : XAResource.rollback ( 31302E31312E31312E31312E746D313537383636343230383735393030303032:31302E31312E31312E31312E746D34 ) on resource orderResource represented by XAResource instance com.mysql.jdbc.jdbc2.optional.MysqlXAConnection@473dd4ea
c.a.icatch.imp.CompositeTransactionImp   : rollback() done of transaction 10.11.11.11.tm157866420875900002
c.a.icatch.imp.CompositeTransactionImp   : rollback() done of transaction 10.11.11.11.tm157866420875900002
m.m.a.RequestResponseBodyMethodProcessor : Using 'text/html', given [text/html, application/xhtml+xml, image/webp, image/apng, application/xml;q=0.9, application/signed-exchange;v=b3;q=0.9, */*;q=0.8] and supported [text/plain, */*, text/plain, */*, application/json, application/*+json, application/json, application/*+json]
m.m.a.RequestResponseBodyMethodProcessor : Writing ["失败"]
o.s.web.servlet.DispatcherServlet        : Completed 200 OK
  • 异常抛出在最后执行事务前
  • 过程:
  1. 请求进入,经由OrderController调用,JtaTransactionManager创建新事务
  2. Atomikos开始获取account库的连接
  3. 查询没有事务处理,随后的更新,XAResource注册了一个事务并生成了一个XID
  4. 然后处理新增订单,同样的Atomikos注册了一个事务并生成了一个XID
  5. 然后报错 create order failed, accountId: 1, errMsg: createOrderCode error
  6. 然后两个资源开始rollback,最后返回"失败"

关于我

  • 坐标杭州,普通本科在读,计算机科学与技术专业,20年6月毕业,疯狂找工作中。。。。
  • 目前处于菜鸟阶段,各位大佬轻喷,小弟正在疯狂学习。
  • 欢迎大家和我交流鸭!!!
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容