数据库事务(Transaction)是由若干个SQL语句构成的一个操作序列,有点类似于Java的synchronized
同步。数据库系统保证在一个事务中的所有SQL要么全部执行成功,要么全部不执行,即数据库事务具有ACID特性:
数据库事务可以并发执行,而数据库系统从效率考虑,对事务定义了不同的隔离级别。SQL标准定义了4种隔离级别,分别对应可能出现的数据不一致的情况:
Isolation Level | 脏读(Dirty Read) | 不可重复读(Non Repeatable Read) | 幻读(Phantom Read) |
---|---|---|---|
Read Uncommitted | Yes | Yes | Yes |
Read Committed | - | Yes | Yes |
Repeatable Read | - | - | Yes |
Serializable | - | - | - |
对应用程序来说,数据库事务非常重要,很多运行着关键任务的应用程序,都必须依赖数据库事务保证程序的结果正常。
关于脏读、不可重复读、幻读的理解
要在JDBC中执行事务,本质上就是如何把多条SQL包裹在一个数据库事务中执行。我们来看JDBC的事务代码:
Connection conn = openConnection(); try { // 关闭自动提交: conn.setAutoCommit(false); // 执行多条SQL语句: insert(); update(); delete(); // 提交事务: conn.commit(); } catch (SQLException e) { // 回滚事务: conn.rollback(); } finally { conn.setAutoCommit(true); conn.close(); }
其中,开启事务的关键代码是conn.setAutoCommit(false)
,表示关闭自动提交。提交事务的代码在执行完指定的若干条SQL语句后,调用conn.commit()
。要注意事务不是总能成功,如果事务提交失败,会抛出SQL异常(也可能在执行SQL语句的时候就抛出了),此时我们必须捕获并调用conn.rollback()
回滚事务。最后,在finally
中通过conn.setAutoCommit(true)
把Connection
对象的状态恢复到初始值。
实际上,默认情况下,我们获取到Connection
连接后,总是处于“自动提交”模式,也就是每执行一条SQL都是作为事务自动执行的,这也是为什么前面几节我们的更新操作总能成功的原因:因为默认有这种“隐式事务”。
联想: python 终端 pymql 用的是不自动提交。
只要关闭了Connection
的autoCommit
,那么就可以在一个事务中执行多条语句,事务以commit()
方法结束。
如果要设定事务的隔离级别,可以使用如下代码:
// 设定隔离级别为READ COMMITTED: conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
如果没有调用上述方法,那么会使用数据库的默认隔离级别。MySQL的默认隔离级别是REPEATABLE_READ
。