0. Pre-knowledge

0.1 Some basic concepts of transactions

  • The previous article described some concepts of transactions and some interface definitions in Spring and the implementation of single data source transactions. Click on the link to see juejin.cn/post/684490…

0.2x /OPEN DTP model

  • X/Open Distributed Transaction Processing Reference Model
  • DTP defines three components: Application Program (AP), Resource Manager (RM), and Transaction Manager (TM).
  1. AP: a program that uses the DTP model
  2. RM: Resource manager
  3. TM: Transaction manager

0.3X /OPEN XA specification

  • Wiki:zh.wikipedia.org/wiki/X/Open…
  • The XA specification is to provide a set of general call interface specifications so that TM can call different RMS, and different RMS can implement their own RMS according to the XA specification.
  • XA uses two-phase commit (2PC) to guarantee that all resources commit or roll back any particular transaction at the same time

MySQL’s InnoDB storage engine supports the XA specification.

0.42PC two-stage submission

  • Wiki: zh.wikipedia.org/wiki/ Two-stage submission
  • Two-phase commit, as the name implies, divides the execution of a transaction into two phases. The first phase is the commit request or vote phase. The transaction manager tells the resource manager the task to be executed and asks if it can do it. Multiple resource managers reply to OK and not OK, respectively; The second phase is the execution phase: the transaction manager receives replies from each resource manager, and if they are all OK, it will do it! If it’s not OK, it’s done.
  • You can look at the wiki for details

0.5 JTA Java Transaction API

  • Java Transaction API (Java Transaction API) is a programming interface for distributed transactions across multiple XA resources proposed by JSR 907 specification. It includes user operation interface, JTATransactionManager, and XAResource.
  • It is an implementation of the XA specification in Java.

  • In the following example we will use the JTA transaction manager to manage transactions for two database operations.

1. Multi-data source transaction management

1.1 Environment Description

1.1.1 Component Description

  • DataSource: Alibaba Druid
  • The Database: the MySQL 5.7
  • SpringBoot: 2.2.2. RELEASE
  • ORM: MyBatis
  • JTA: Atomikos
  • Making: github.com/imyiren/tra…

1.1.2 Project Key Dependencies

    <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>
Copy the code

1.1.3 Multiple data sources

  • The data source uses different tables from the two databases
  • It’s all pooled with Druid and managed with Atomikos

1.1.4 JTA tools

  • SpringBoot is officially available in Atomikos and Bitronix, as well as jTA-enabled Web servers. (Tomcat is not supported)
  • Note in the SpringBoot documentation: When a JTA environment is detected, Spring’s JtaTransactionManager is used to manage transactions. JMS, DataSource, and JPA have been upgraded to support XA transactions. You can participate in distributed transactions using standard Spring usages such as @Transactional. If you are in a JTA environment and still want to use local transactions, you can disable JTA auto-configuration by setting the spring.jta.enabled property to false.

1.2 Example Service Description

  • Simple logic, two tables, one in two different libraries, and a service method that operates on the data in both libraries.

1.3 Configuring Multiple Data Sources

1.3.1 Configuration File

  • The first table: is the account table
  • Chapter 2 table: is the order table
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
Copy the code

1.3.2 Bean registered

  • It mainly includes the following steps
  1. The DataSource, SqlSessionFactory, and SqlSessionTemplate beans are registered
  2. Then specify the Mapper location of the table and set template to the one you registered.
  • The similarity of the order library is omitted here

  • Points to note:
  1. DruidDataSource cannot use DruidDataSource directly. Atomikos is used to package Druid’s DruidXADataSource to support the XA specification
  2. If you don’t want to use Druid, consider using MysqlXADataSource (I haven’t tried it yet)
  3. The mapping of registered beans must be correct
@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(a) {
        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 newSqlSessionTemplate(sqlSessionFactory); }}Copy the code

  • If the configuration is correct, the following log information is displayed
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 : The Loaded jar: file: / Users/feng/m2 / repository/com/atomikos/the transactions / 4.0.6 / the 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... Slightly C.A.I catch. The 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.I. Catch the provider. The 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 XAResourceCopy the code
  • Initialize the Druid data sources for the two Atomikos wrapped druids.

  • Then set the atomikos parameters, which are all default

  • Finally, the XAResource is refreshed

  • At this point, the configuration is complete. For those who are curious, there is no JTA code because SpringBoot uses the starter introduced when JTA is used

1.4 Transaction Instance

  • Simply simulate the order generation payment process, deduct a percentage of money from the account, and then add a percentage of orders.
  • The way you program is pretty much the same as Spring transactions.

1.4.1 Implementation code

@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 "The user does not exist!";
        }
        // Deduct user fees
        accountDO.setBalance(accountDO.getBalance().subtract(orderInfoDO.getAmount()));
        accountDAO.updateByPrimaryKey(accountDO);
        orderInfoDAO.insertSelective(orderInfoDO);

        return "Success";
    }

    @Override
    public String createOrderCode(OrderInfoDO orderInfoDO) {
        TransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
        // Get transaction to start business execution
        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 "The user does not exist!";
            }
            // Deduct user fees
            accountDO.setBalance(accountDO.getBalance().subtract(orderInfoDO.getAmount()));
            accountDAO.updateByPrimaryKey(accountDO);
            orderInfoDAO.insertSelective(orderInfoDO);
            error("createOrderCode error");
            transactionManager.commit(transaction);

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

        return "Failure";
    }

    public static void error(String msg) {
        throw newRuntimeException(msg); }}Copy the code

1.4.2 Process

  • Log parameters are set to:logging.level.root=DEBUGIf you do not set it, you will see very little transaction log.

  • Logging calls success: 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 synchronizationfor 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 ) forThe transaction 10.11.11.11. Tm157866358813800001 C.A.D atasource. 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 ) forThe transaction 10.11.11.11. Tm157866358813800001 c.a. tomikos. JDBC. AtomikosConnectionProxy: atomikos connection proxyforcom.mysql.jdbc.JDBC4Connection@73c7f762: calling prepareStatement(select.... Slightly I.I.T.T.D.A.A.S electByPrimaryKey: = = > Parameters: 1 (Long) I.I.T.T.D.A.A.S electByPrimaryKey: < = = 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 accountsetnickname = ? , username = ? , `password` = ? , balance = ? , create_time = ? , update_time = ?where id = ?
c.a.icatch.imp.CompositeTransactionImp   : registerSynchronization ( com.atomikos.jdbc.AtomikosConnectionProxy$JdbcRequeueSynchronization@853ac8fa ) forThe transaction 10.11.11.11. Tm157866358813800001 c.a. tomikos. JDBC. AtomikosConnectionProxy: atomikos connection proxyforcom.mysql.jdbc.JDBC4Connection@73c7f762: calling prepareStatement(update account i.i.t.t.d.a.A.updateByPrimaryKey : ==> Parameters: 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 synchronizationfor 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 forcom.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 )forThe transaction 10.11.11.11. Tm157866358813800001 C.A.D atasource. 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 ) forThe transaction 10.11.11.11. Tm157866358813800001 c.a. tomikos. JDBC. AtomikosConnectionProxy: atomikos connection proxyforcom.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), Buy Clothes (String), 100(BigDecimal), 2020-01-10T21:39:48.134(LocalDateTime), The 2020-01-10 T21:39:48. 134 (LocalDateTime) I.I.T.T.D.O.O.I nsertSelective: < = = 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 proxyfor 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 the application of transaction 10.11.11.11. Tm157866358813800001 C.A.D atasource. 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/ sign-exchange; V = b3; q = 0.9, * / *; Supported [text/plain, */*, text/plain, */*, application/json, application/*+json, application/json, application/*+json] m.m.a.RequestResponseBodyMethodProcessor : Writing ["Success"]
o.s.web.servlet.DispatcherServlet        : Completed 200 OK
Copy the code
  • Process:
  1. The request comes in, is invoked by The OrderController, and the JtaTransactionManager creates a new transaction
  2. Atomikos starts getting connections to the Account library
  3. The query has no transaction processing, and a subsequent update, XAResource registers a transaction and generates an XID
  4. The new order is then processed, and the same Atomikos registers a transaction and generates an XID
  5. Finally prepare, then submit.
  6. The request returns 200 on success.

  • Failure logging calls: 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
Copy the code
  • The exception is thrown before the final transaction is executed
  • Process:
  1. The request comes in, is invoked by The OrderController, and the JtaTransactionManager creates a new transaction
  2. Atomikos starts getting connections to the Account library
  3. The query has no transaction processing, and a subsequent update, XAResource registers a transaction and generates an XID
  4. The new order is then processed, and the same Atomikos registers a transaction and generates an XID
  5. Create Order failed, accountId: 1, errMsg: createOrderCode error is displayed
  6. And then both resources start to rollback, and finally return “failed”

  • Think you can point a thumbs-up 👍 Thanks!

About me

  • Majoring in Computer Science and technology, general university, Hangzhou.
  • Graduated in 20 years, mainly engaged in back-end development of Java technology stack.
  • GitHub: github.com/imyiren
  • Blog : imyi.ren