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

0. 前置知识

0.1 事务的一些基本概念

  • 上篇说了事务的一些概念和Spring中的一些接口定义以及单数据源事务的实现具体的自己点下链接看吧
    https://juejin.im/post/5e17013de51d453cbc4613a3

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 组件说明

  • DataSource: Alibaba Druid
  • Database: MySQL 5.7
  • SpringBoot: 2.2.2.RELEASE
  • ORM: MyBatis
  • JTA: Atomikos
  • GitHub: https://github.com/imyiren/transaction-example/tree/master/two-data-source

1.1.2 项目关键依赖

    
            org.mybatis.spring.boot
            mybatis-spring-boot-starter
            2.1.1
        
        
        
            org.springframework.boot
            spring-boot-starter-jta-atomikos
        
        
            com.alibaba
            druid-spring-boot-starter
            1.1.21
        

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 如果不设置,你基本看不到什么事务日志。

  • 成功日志 调用:http://localhost:8080/api/order/create
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.

  • 失败日志 调用:http://localhost:8080/api/order/create/code
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月毕业,疯狂找工作中。。。。
  • 目前处于菜鸟阶段,各位大佬轻喷,小弟正在疯狂学习。
  • 欢迎大家和我交流鸭!!!

你可能感兴趣的:(事务—【02】SpringBoot使用JTA Atomikos实现多数据源事务管理)