Mysql事務回滾的問題探究

Mysql事務回滾的問題探究

前提

​ XXX平臺導出的mysql建表語句沒有指定存儲引擎,而mysql默認使用MyISAM

​ 但是MyISAM是不支持事務的。在mysql中,唯有InnoDb是支持事務的。

目的

​ 驗證Spring提供的標籤@Transactional,以及XXX提供的TransactionComponent組件在不同的存儲引擎下的工作實況。

建表語句

​ 借用XXX-BATCH工程中的“batch_cli_user”表部分字段進行實驗。

CREATE TABLE `BATCH_CLI_USER` (
	`protocol_no`  VARCHAR(64) NOT NULL COMMENT '協議號',
	`user_id`  VARCHAR(32) COMMENT '用戶標識,錄入人ID',
	`user_name`  VARCHAR(80) COMMENT '用戶名稱',
	`contact`  VARCHAR(32) COMMENT '聯繫人',
	`telephone`  VARCHAR(16) COMMENT '電話號碼',
	`mobile_no`  VARCHAR(16) COMMENT '電話號碼',
	CONSTRAINT `pk_BATCH_CLI_USER` PRIMARY KEY (`protocol_no`)
) ENGINE = MYISAM;

​ 以及使用Innodb存儲引擎的建表語句

CREATE TABLE `BATCH_CLI_USER` (
	`protocol_no`  VARCHAR(64) NOT NULL COMMENT '協議號',
	`user_id`  VARCHAR(32) COMMENT '用戶標識,錄入人ID',
	`user_name`  VARCHAR(80) COMMENT '用戶名稱',
	`contact`  VARCHAR(32) COMMENT '聯繫人',
	`telephone`  VARCHAR(16) COMMENT '電話號碼',
	`mobile_no`  VARCHAR(16) COMMENT '電話號碼',
	CONSTRAINT `pk_BATCH_CLI_USER` PRIMARY KEY (`protocol_no`)
) ENGINE = INNODB;

實驗思路

​ 通過兩次對相同主鍵的插入,若事務生效,則在第二次插入報主鍵衝突的時候,應當發生回滾,使第一條的插入語句也被回滾,若事務不生效,則查詢後可以觀察到該記錄。

實驗過程

測試代碼

​ 如果發送事務回滾,則預期爲null正確。

@Test
public void testRollbackForMysql() throws Exception {    batchCliUserMapper.deleteByPrimaryKey("testProtocolNo004");    
 BatchCliUser batchCliUser = new BatchCliUser();    
 batchCliUser.setUserId("testUser004");    
 batchCliUser.setProtocolNo("testProtocolNo004");    batchCustomerInfoService.testRollbackForMysql(batchCliUser);    
 BatchCliUser result1 = batchCliUserMapper.selectByPrimaryKey("testProtocolNo004");    Assert.assertTrue(null == result1);
}

1. MyISAM與@Transactional

代碼

@Override@Transactional(rollbackFor = Exception.class)
public void testRollbackForMysql(BatchCliUser batchCliUser) throws Exception {    
    // 開啓事務,連續插入兩次,看事務是否會回滾掉第一次的插入    
    LOGGER.info("開始第一次插入");    
    batchCliUserMapper.insertSelective(batchCliUser);
    LOGGER.info("開始第二次插入");    
    batchCliUserMapper.insertSelective(batchCliUser);}

結果

​ 代碼在第二次插入時拋出org.springframework.dao.DuplicateKeyException。但是在數據庫中仍然可以查詢到該記錄,回滾並未生效。

在這裏插入圖片描述

2. MyISAM與TransactionComponent組件

代碼

@Override
public void testRollbackForMysql(BatchCliUser batchCliUser) throws Exception {    
    // 顯式開啓事務,連續插入兩次,看事務是否會回滾掉第一次的插入    
    try {        
        transactionComponent.begin();        
        LOGGER.info("開始第一次插入");        
        batchCliUserMapper.insertSelective(batchCliUser);        
        LOGGER.info("開始第二次插入");        
        batchCliUserMapper.insertSelective(batchCliUser);        
        transactionComponent.commit();    
    } catch (Exception e) {        
        transactionComponent.rollback();        
        throw e;    
    }
}

結果

​ 與實驗1相同,回滾並未生效。

3. INNODB與@Transactional

代碼

​ 與實驗1相同

結果

​ 成功回滾,數據庫中查無記錄。

4. INNODB與TransactionComponent組件

代碼

​ 與實驗2相同

結果

​ 與實驗3相同,成功回滾,數據庫中查無記錄。

後續探究

TransactionTemplate

​ Spring提供的事務管理模板,驗證可以事務回滾。【且相對XXX提供的組件,更方便設置隔離級別,事務傳播特性等】

	@Autowired
    public TransactionTemplate transactionTemplate;  

	@Override
	public void testRollbackForMysql(BatchCliUser batchCliUser) throws Exception {        
        // 顯式開啓事務,連續插入兩次,看事務是否會回滾掉第一次的插入        
        transactionTemplate.execute(new TransactionCallbackWithoutResult(){           
            @Override protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                LOGGER.info("開始第一次插入");
                batchCliUserMapper.insertSelective(batchCliUser);
                LOGGER.info("開始第二次插入");
                batchCliUserMapper.insertSelective(batchCliUser);
            }        
        });
    }

​ xml中的Bean配置

<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">   
    <property name="transactionManager" ref="testTransactionManager"></property>
</bean>

深入探究

  1. 三種方式均可完成回滾,那麼其到底有什麼共同點,在DataSourceTransactionManager類中有如下方法,在debug時發現:
protected void doRollback(DefaultTransactionStatus status) {    DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)status.getTransaction();
   	Connection con = txObject.getConnectionHolder().getConnection();
   	if (status.isDebug()) {
           this.logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
       } try {
           con.rollback();
       } catch (SQLException var5) {
           throw new TransactionSystemException("Could not roll back JDBC transaction", var5);
       }
   }

其核心代碼在con.rollback()這一行中,交由各個Connection接口的實現類自己實現。

PS: @Transactional註解還需要配置代理,配置如下後:

<!-- 事務管理器 -->
<bean id="testTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">   
    <property name="dataSource" ref="gapsDataSource"/>
</bean>
<tx:annotation-driven transaction-manager="testTransactionManager" proxy-target-class="true"/>

配置好後@Transactional就可以正常回滾了。

  1. Mysql事務中到底怎麼完成回滾?

    調試con.rollback()方法中,可以看到調用了以下方法:

    private void rollbackNoChecks() throws SQLException {
        try {        
            synchronized(this.getConnectionMutex()) {
                if (!(Boolean)this.useLocalTransactionState.getValue() || this.session.getServerSession().inTransactionOnServer()) {
                    this.session.execSQL((Query)null, "rollback", -1, (NativePacketPayload)null, false, this.nullStatementResultSetFactory, this.database, (ColumnDefinition)null, false);
                }
            }
        } catch (CJException var5) {
            throw SQLExceptionsMapping.translateException(var5, this.getExceptionInterceptor());
        }
    }
    

    方法中執行了execSQL((Query)null, “rollback”,***),去完成了回滾操作。

結論

  1. @Transactional、TransactionComponent、TransactionTemplate均可以完成在Mysql的INNODB存儲引擎上的事務回滾,但不能在默認的MyISAM存儲引擎完成回滾

  2. TransactionComponent、TransactionTemplate使用方法類似,需要手動使用代碼控制事務。@Transactional註解在開發上一般使用在實現類的方法上,使用方便,代碼量少,粒度較顯式使用會粗一點。

  3. 三種方法均需配置ResourceTransactionManager的Bean交於Spring管理事務。

  4. 開發時導出數據庫建表語句建議直接顯式聲明其存儲引擎,以防有些數據庫並未設置默認引擎爲INNODB。

    (修改數據庫默認存儲引擎:在配置文件my.cnf中的 [mysqld] 下面加入default-storage-engine=INNODB)

    default-storage-engine=INNODB
    

    可以在數據庫執行sql show engines查看默認的存儲引擎:

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章