我对autocommit以及select语句是否需要加事务的一点理解

本文探讨了MySQL中的autocommit特性和事务使用,解释了autocommit如何影响DML语句,并讨论了在不同隔离级别下读写操作的一致性和原子性。在执行SQL语句时,数据库是否会自动开启事务存在争议,但实践表明,即使不显式开启事务,DML操作的结果也会被提交或回滚。在使用Hibernate时,对于查询操作无需手动管理事务,而对于DML操作则建议显式控制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在复习hibernate以及数据库事务和隔离级别的相关知识,有了一点新的理解。mysql可以通过下面的命令:查询和修改当前客户端连接的autocommit。0代表不自动提交,1代表自动提交。


什么是自动提交呢?对于insert、update、delete这些DML语句来说,如果没有提交,被修改的记录处于uncommited状态的;如果提交了,那么处于commited状态的。下面的客户端1和2设置的事务隔离级别都是repeatable read,所以客户端2看不到客户端1的修改。如果客户端1打开了autocommit,那么客户端2就可以查看到客户端1修改后的结果。

     


mysql如果我们想开启一个事务,可以通过start  transaction;如果提交这个事务用commit;如果需要放弃回滚,那么可以使用rollback。

#提交一个事务
start  transaction;
	#some sql statements
commit;

#回滚事务
start  transaction;
	#some sql statements
rollback;

可以看到autocommit和事务很像,我自己认为:执行第一条SQL语句的时候,如果我们没有显示地开启事务,那么数据库服务器会自动帮我们开启一个事务;如果打开了autocommit,每一条sql执行结束的时候,数据库服务器会自动关闭事务。如果关闭了autocommit,那么数据库就不会自动帮我们提交事务。


也就是说:如果开启了autocommit,数据库服务器自动开启事务(每一条sql语句开始执行的时候),自动提交事务(sql语句执行成功),自动回滚事务(sql语句执行失败)。很显然:autocommit没有什么实际意义,如果要使用事务,就必需关闭autocommit,不然每一条sql都是一个独立的事务,而实际上事务包含了一组sql语句。最佳实践:对于DML操作,我们需要显示开始、显示提交或者回滚事务,哪儿怕事务里面只有一条sql语句

public void testHibernateSave() {
	Student student = new Student();
	student.setName("saveWithHibernate");
	student.setAge(3);

	Session session = sessionFactory.openSession();
	session.beginTransaction();//开启事务
	session.save(student);

	session.save(student);

	session.getTransaction().commit();//提交事务
	session.close();
}


现在我们理解autocommit和事务了,那我们再看一下select语句,select语句需要加事务吗?
public void testSelect() {
	Session session = sessionFactory.openSession();
	Student student = (Student) session.get(Student.class, 100);
	System.out.println(student);
	session.close();
}


public void testSelectWithTransaction() {
	Session session = sessionFactory.openSession();
	session.beginTransaction();//开启事务

	Student student = (Student) session.load(Student.class, 100);

	System.out.println(student);
	
	session.getTransaction().commit();//提交事务
	session.close();
}
上面的2种查询方式,都能从数据库查出我们需要的结果。那到底select的时候需不要加事务呢?这个问题,网上有很多争论,有人说需要加,有人说不需要,比如iteye上的这个帖子。我来谈下自己的看法,不一定对,有错误的话欢迎指正。

读操作最重要的是什么呢?一致性。写操作最重要的是什么吗呢?原子性。如果一个客户端(占用一个数据库连接),只需执行要一条select,那么显然加不加事务没有任何影响。如果一个客户端,需要执行多条select,对于这个客户端来说,最重要的就是读的一致性了。比如我第一次读取这条记录,第二次再次读取这条记录,客户端肯定希望这2次的读取结果是一模一样的。



客户端1的事务隔离级别是:REPEATABLE-READ,关闭了autocommit,如果客户端1没有显示地commit,那么永远也看不见客户端2修改后的数据。如果客户端1显示提commit之后,再次查询,就能够读取到客户端2修改后的结果了。我们知道:一个数据库连接可以开启多个数据库事务。至于一个数据库事务里面多次查询结果是否一致,是取决于事务隔离级别的。不同的隔离级别能够解决不同的数据库并发问题:



所以我认为:如果我们没有显示地开启事务,那么当执行第一条sql语句(不管是select,还是DML)都会开启一个事务。如果打开了autocommit,那么sql执行成功后会自动提交;如果关闭了autocommit,那么我们必须要显示地commit或者rollback。

public void testSelect() {
	Session session = sessionFactory.openSession();
	Student student = (Student) session.get(Student.class, 100);
	System.out.println(student);
	session.close();
}

hibernate默认会关闭autocommit,上面的代码没有显示开启和提交事务。当执行select的时候,底层的数据库连接会自动开启事务,我们没有显示地提交事务,这是因为session.close会释放底层的数据库连接,数据库连接被释放,没有提交的事务会被自动回滚。所以使用hibernate的时候,DML操作我们需要显示地开启事务和提交事务,如果是查询操作不需要显示地开启和提交事务


写到这里,有一个地方是存在争议的:如果没有显示地开启事务,mysql数据库到底会不会在执行第一条SQL的时候自动开始事务。从上面的测试结果来看,应该是会的,或者说至少也会开启一个类似事务的东东(出于性能的考虑)。因为我们没有显示地开启事务,执行DML语句后commit,这些修改确实被永久保存到了数据库。而且执行select后,如果不commit,那么后续的select永远也看不到另一个客户端的修改结果。从表现上来看,这跟事务没有什么两样。


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值