关键字 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命令
-
DDL – Data Definition Language
数据定义语言
create drop alter truncate comment rename
-
DQl – Data Query Language
数据查询语言
示例: select
-
DML – Data Manipulation Language
数据处理语言
示例: insert update delete
-
DCL – Data Control Language
数据控制语言
示例: grant revoke
-
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()中就会进行事务的回滚。