MySQL中的autocommit和事务

2,601 阅读4分钟

关键字 mysql | autocommit | 事务

预备点

. MySQL数据库引擎

设置为InnoDB

. 数据库隔离级别

设置为REPEATABLE-READ
--查看当前会话隔离级别
show variables like 'transaction_isolation';
select @@tx_isolation;
--查看系统当前隔离级别
select @@global.tx_isolation;

--设置当前会话隔离级别
set session transaction_isolation='READ-UNCOMMITTED';
set session transaction_isolation='READ-COMMITTED';
set session transaction_isolation='REPEATABLE-READ';
set session transaction_isolation='SERIALIZABLE';

. autocommit

-- 查看autocommit是否开启
SELECT @@autocommit;
show variables like 'autocommit';
-- 关闭autocommit
SET autocommit = 0;
-- 开启autocommit
SET autocommit = 1;

autocommit在MySQL数据库中是默认开启的;

注意:autocommit只涉及数据库是否帮你提交事务,如果你没有显示的开启一个事务,那么数据库会帮你自动开启一个事务的

. SQL命令

  1. DDL – Data Definition Language

    数据定义语言

    create drop alter truncate comment rename

  2. DQl – Data Query Language

    数据查询语言

    示例: select

  3. DML – Data Manipulation Language

    数据处理语言

    示例: insert update delete

  4. DCL – Data Control Language

    数据控制语言

    示例: grant revoke

  5. TCL – Transaction Control Language

    事务控制语言

    示例: commit– commits a Transaction.

    rollback– rollbacks a transaction in case of any error occurs.

    savepoint– sets a savepoint within a transaction.

    set transaction– specify characteristics for the transaction.

DML提交事务的意义

总结:

对于DML数据操作,我们必须要记住提交事务,如果autocommit为1的话,当然就不用我们自己操心了!!数据库会帮我们提交的!!但是在我们的Hibernate和MyBatis等持久层框架中,进行DML操作时我们必须要手动开启事务,并且手动提交事务!!因为在Hibernate和MyBatis等持久层框架中,它们处理DML语句的时候会自动设置autocommit=0;如果DML中不进行手动提交事务,那么最后事务就会进行回滚。

MyBatis事务管理源码

public class DefaultSqlSessionFactory implements SqlSessionFactory {
  @Override
  public SqlSession openSession() {
    //关键代码 autoCommit : 是否开启事务 ( false 为开,true 为不开)
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }
}

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      final Executor executor = configuration.newExecutor(tx, execType);
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

MyBatis-Spring事务管理器源码

spring-jdbc-5.1.2.RELEASE.jar

public class DataSourceTransactionManager extends AbstractPlatformTransactionManager
		implements ResourceTransactionManager, InitializingBean {
  protected void doBegin(Object transaction, TransactionDefinition definition) {
      // Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
      // so we don't want to do it unnecessarily (for example if we've explicitly
      // configured the connection pool to set it already).
      if (con.getAutoCommit()) {
          //** 关键代码1 **
          txObject.setMustRestoreAutoCommit(true);
          if (logger.isDebugEnabled()) {
              logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
          }
          con.setAutoCommit(false);
      }
  }
}

mybatis-spring-2.0.0.jar

public class SpringManagedTransaction implements Transaction {
   private void openConnection() throws SQLException {
      this.connection = DataSourceUtils.getConnection(this.dataSource);
       //** 关键代码2 **
      this.autoCommit = this.connection.getAutoCommit();
      this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.connection, this.dataSource);

      LOGGER.debug(() ->
          "JDBC Connection ["
              + this.connection
              + "] will"
              + (this.isConnectionTransactional ? " " : " not ")
              + "be managed by Spring");
    }
    
    public Connection getConnection() throws SQLException {
      if (this.connection == null) {
        openConnection();
      }
      return this.connection;
    }
  }

问: 如果你要问我为什么不设置autocommit=1自动提交,反而还要很麻烦的自己开启事务,并且自己手动提交事务? 答:为什么不直接设置为autocommit=1??因为autocommit是针对于单条sql语句的自动提交。反而我们真实写项目的时候,用到的事务都是包含多条sql语句,所以我们不得不自己手动显示开启事务,并且手动进行事务提交!!以此来创建一个事务作用边界。

最后:DML提交事务的意义:事务是什么?事务是多个sql操作的集合,而开启事务就相当于开启一个线程,多个sql操作就相当于在这个线程里面执行多个任务,而我们的提交事务就相当于保存数据,回滚事务就相当于不保存数据。

对于MyISAM等不支持事务的数据库引擎来说,就不会涉及到这些事务开启和提交的问题,当然每一条sql语句也相当于一个线程了。

好!!非常Nice!!

DQL是否有必要提交事务

总结1

. 在REPEATABLE-READ事务隔离级别中,进行DQL操作时,我们往往需要commit、回滚或者重新开启事务来结束当前事务,以此获取新事务解决数据老化问题。 然而在Hibernate和MyBatis等持久层框架中,你会发现往往不需要我们去管理DQL的事务。我们不需要手动开启事务,也不需要提交事务。那么框架是怎么处理这种问题的呢?而且Hibernate和MyBatis的autocommit总是被框架设置为0。

总结2

. DML要提交事务后才能够把数据持久化。

. 框架中DML要我们手动开启事务和提交事务。

. 框架中DQL不需要我们手动开启事务或者提交事务。我们不做同样能够读取到最新数据!!DQL的事务开启或者提交是框架帮我们做!!

. DQL不同隔离级别查询的值会不同,针对于MySQL,InnoDB的REPEATABLE_READ,其保证了同一个事务里,查询的结果和开启事务时并且第一次查询数据时的数据总是一样的。

. 这里补充一点,MyBatis在session.close()中如果session.commit()没有被执行,那么在session.close()中就会进行事务的回滚。

参考

blog.csdn.net/marvel__dea…

blog.csdn.net/aitangyong/…

www.cnblogs.com/henryhappie…

www.geeksforgeeks.org/sql-ddl-dql…