Транзакции — SpringBoot использует JTA Atomikos для реализации управления транзакциями с несколькими источниками данных.

Spring Boot

0. Предварительное знание

0.1 Некоторые основные концепции транзакций

  • В прошлой статье я рассказал о некоторых концепциях транзакций, некоторых определениях интерфейса в Spring и реализации транзакций с одним источником данных.nuggets.capable/post/684490…

Модель 0.2 X/OPEN DTP

  • Полное название: Эталонная модель распределенной обработки транзакций X/Open, которая представляет собой модель распределенной обработки транзакций.
  • Три компонента, определяемые DTP: AP (прикладная программа), RM (менеджер ресурсов, диспетчер ресурсов), TM (менеджер транзакций, диспетчер транзакций).
  1. AP: программа, использующая модель DTP.
  2. RM: диспетчер ресурсов (может пониматься как система базы данных, система очереди сообщений)
  3. ТМ: менеджер транзакций

0,3 X/OPEN XA Спецификация

  • вики:Это.Wikipedia.org/wiki/X/open…
  • Спецификация XA должна предоставить набор общих спецификаций интерфейса вызова, чтобы TM мог вызывать разные RM, а также позволял различным RM реализовывать свои собственные RM в соответствии со спецификацией XA.
  • XA использует двухфазную фиксацию (2PC), чтобы гарантировать одновременную фиксацию или откат любой конкретной транзакции всеми ресурсами.

Механизм хранения MySQL InnoDB поддерживает спецификацию XA.

0.4 Двухфазная фиксация 2PC

  • wiki: en.wikipedia.org/wiki/Двухэтапная фиксация
  • Двухэтапная фиксация, как следует из названия, делит выполнение транзакции на две фазы.Первая фаза — это запрос на отправку или фаза голосования.Менеджер транзакций сообщает менеджеру ресурсов задачу, которую нужно выполнить, и спрашивает, может ли она быть выполнена. ? Несколько менеджеров ресурсов отвечают OK и не OK соответственно; второй этап — это этап выполнения: менеджер транзакций получает ответы от каждого менеджера ресурсов, если все в порядке, то сделайте это! Если это не нормально, не делайте этого.
  • Подробности смотрите в вики

0.5 JTA API транзакций Java

  • Полное название: API транзакций Java, предложенный спецификацией 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. (Не поддерживается Tomcat)
  • Примечание из документации SpringBoot: при обнаружении среды JTA для управления транзакциями используется Spring JtaTransactionManager.JMS,DataSource,JPAОбновлен для поддержки транзакций XA. Можно участвовать в распределенных транзакциях, используя стандартное использование Spring (например, @Transactional). Если вы находитесь в среде JTA и по-прежнему хотите использовать локальные транзакции, вы можете отключить автоматическую настройку JTA, установив для свойства spring.jta.enabled значение false.

1.2 Описание бизнеса экземпляра

  • Простая логика, две таблицы находятся в двух разных библиотеках, а затем сервисный метод работает с данными двух библиотек.

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 Регистрация компонента

  • В основном включают следующие шаги
  1. Зарегистрируйте bean-компоненты, соответствующие DataSource, SqlSessionFactory и SqlSessionTemplate соответственно.
  2. Затем укажите расположение маппера таблицы и установите шаблон на тот, который вы зарегистрировали.
  • Сходства библиотеки заказов здесь опущены.

  • Обратите внимание:
  1. DataSource не может напрямую использовать DruidDataSource, предоставленный Druid, вам нужно использовать atomikos для переноса DruidXADataSource, предоставленного Druid, для поддержки спецификации XA.
  2. Если вы не хотите использовать Druid, рассмотрите возможность использования MysqlXADataSource (я не пробовал)
  3. Соответствующее отношение зарегистрированных бинов должно быть правильным
@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
  • Сначала инициализируйте два источника данных Druid, обернутых Atomikos,

  • Затем установите параметры атомикоса, которые все по умолчанию

  • Последнее обновление XAResource

  • На этом этапе, после завершения настройки, некоторым людям может быть любопытно, что нет кода JTA, потому что стартер, представленный, когда SpringBoot использует JTA, не

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 начинает получать подключения к библиотеке учетных записей
  3. Запрос не обрабатывается, и последующие обновления, XAResource регистрирует транзакцию и генерирует XID
  4. Затем для обработки нового заказа тот же Атомикос регистрирует транзакцию и генерирует 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 начинает получать подключения к библиотеке учетных записей
  3. Запрос не обрабатывается, и последующие обновления, XAResource регистрирует транзакцию и генерирует XID
  4. Затем для обработки нового заказа тот же Атомикос регистрирует транзакцию и генерирует XID.
  5. Затем сообщается об ошибке создать заказ не удалось, accountId: 1, errMsg: ошибка createOrderCode
  6. Затем два ресурса начинают откат и, наконец, возвращают «сбой».

  • Ставьте палец вверх, если считаете, что сможете 👍 Спасибо!

обо мне

  • Координатор Ханчжоу, специализирующийся в области компьютерных наук и технологий в общеобразовательных колледжах и университетах.
  • Окончил 20 лет, в основном занимается внутренней разработкой стека технологий Java.
  • GitHub: github.com/imyiren
  • Blog : imyi.ren