您的位置:首页 > 其它

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

2016-01-08 13:17 459 查看
最近在复习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永远也看不到另一个客户端的修改结果。从表现上来看,这跟事务没有什么两样。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息