第十章 事务(Transactions)
事务用于在并发访问期间提供数据完整性,正确的应用程序语义和一致的数据视图。 需要所有JDBC兼容驱动程序来提供事务支持。 JDBC API中的事务管理反映了SQL:2003规范,并包含了这些概念
- 自动提交模式
- 事务隔离级别
- 保存点
本章介绍与单个Connection对象关联的事务语义。 涉及多个Connection对象的事务在第12章“分布式事务”中讨论。
10.1 交易界限和自动提交
什么时候开始一个新的事务是由JDBC驱动程序或底层数据源隐含的决定,虽然一些数据源实现了一个明确的“begin transaction”语句,但是没有JDBC API可以实现,通常,当当前的SQL语句需要一个并且没有事务已经存在的时候,一个新的事务被启动。 一个给定的SQL语句是否需要一个事务也由SQL:2003指定。
Connection属性auto-commit指定何时结束事务。 一旦该语句完成,启用自动提交会在每个单独的SQL语句之后导致事务提交。 一个语句被认为是“完成”的点取决于SQL语句的类型以及应用程序在执行它之后执行的操作:
- 对于“数据操作语言”(DML)语句(如“插入”,“更新”,“删除”和“DDL”语句),语句在执行完毕后立即完成
- 对于Select语句,当关联的结果集关闭时,该语句将完成。
- 对于CallableStatement对象或返回多个结果的语句,当所有关联的结果集都已关闭时,该语句已完成,并且已检索所有更新计数和输出参数
10.2 禁用自动提交模式
当禁用自动提交时,每个事务必须通过调用Connection方法提交或通过调用Connection方法回滚来显式回滚来显式提交。 这适用于在驱动程序上方的层中进行事务管理的情况,例如
- 当应用程序需要将多个SQL语句组合成一个事务
- 当事务由应用程序服务器管理时
默认值是在创建Connection对象时启用自动提交模式。 如果自动提交的值在事务中间更改,则提交当前事务。 如果调用了setAutoCommit,并且auto-commit的值不会从当前值更改,则被视为无操作
如第12章“分布式事务”所述,为参与分布式事务的连接启用自动提交是一个错误。
10.2 事务隔离等级
事务隔离级别指定事务中的语句“可见”哪些数据。 它们通过定义在相同目标数据源之间的交易之间可以进行什么交互(如果有的话)直接影响并发访问级别。 并发事务之间的可能交互分类如下:
-
当允许事务查看未提交的数据更改时,会发生脏读。 换句话说,交易之前所做的更改在提交之前在事务外部可见。 如果更改回滚而不是提交,则可以根据不正确的临时数据完成其他事务的工作。
不可重复读取时发生:
1.事务A读取一行
2.事务B更改行
3,事务A第二次读取相同的行并获得不同的结果 -
幻象读取发生时
1.事务A读取满足WHERE条件的所有行
2.事务B插入一个满足相同条件的附加行
3.事务A重新评估WHERE条件并拾取附加的“幻像”行
JDBC通过添加TRANSACTION_NONE来增强 SQL:2003定义的四个级别的事务隔离。 从最少限制到最严格的交易隔离级别是:
1,TRANSACTION_NONE :表示驱动程序不支持事务,这意味着它不是JDBC兼容的驱动程序。
2,TRANSACTION_READ_UNCOMMITTED:允许事务查看未提交的数据更改。 这意味着脏的读取,不可重复的读取和幻像读取是可能的
3,TRANSACTION_READ_COMMITTED:意味着事务之前进行的任何更改在事务发生之前都不可见,直到事务被提交。 这样可以防止脏读,但是不可重复的读取和幻像读取仍然是可能的。
4,TRANSACTION_REPEATABLE_READ:不允许脏读和不可重读读。 幻影读取仍然是可能的。
5,TRANSACTION_SERIALIZABLE:指定防止脏读,不可重复读取和幻像读取。
10.2.1 使用 setTransactionIsolation 方法
Connection对象的默认事务级别由提供连接的驱动程序决定。 通常,它是底层数据源支持的默认事务级别
提供了Connection方法setTransactionIsolation,以允许JDBC客户端更改给定Connection对象的事务隔离级别。 新的隔离级别对于会话的剩余部分或直到下一次调用setTransactionIsolation方法仍然有效
在事务中间调用setTransactionIsolation方法的结果是实现定义的
方法getTransactionIsolation的返回值应该反映实际发生时隔离级别的变化。 建议驱动程序实现setTransactionIsolation方法来更改从下一个事务开始的隔离级别。 提交当前事务以使效果立即也是有效的实现
给定的JDBC驱动程序可能不支持所有四个事务隔离级别(不计算TRANSACTION_NONE)。 如果驱动程序不支持在调用setTransactionIsolation中指定的隔离级别,则可以替换更高级别,更严格的事务隔离级别。 如果驱动程序无法替代较高的事务级别,则会引发SQLException。 DatabaseMetaData方法supportsTransactionIsolationLevel可以用于确定驱动程序是否支持给定的级别
10.2.2 性能注意事项
随着事务隔离级别的增加,需要更多的锁定和其他DBMS开销来确保正确的语义。 这反过来降低了可以支持的并发访问的程度。 因此,当应用程序使用更高的事务隔离级别时,应用程序可能会看到降低的性能。 因此,无论是应用程序本身还是应用程序服务器的一部分,事务管理器在确定哪个事务隔离级别合适时,应该对数据一致性的需求与性能要求进行权衡。
10.3 Savepoints
保存点通过标记事务中的中间点来提供对事务的更细粒度的控制。 一旦设置了保存点,事务可以回滚到该保存点,而不影响以前的工作
DatabaseMetaData.supportsSavepoints方法可用于确定JDBC驱动程序和DBMS是否支持保存点
10.3.1 设置和回滚至 保存点
Connection.setSavepoint方法可用于在当前事务中设置保存点。 如果调用了setSavePoint并且没有活动的事务,则事务将被启动。 Connection.rollback方法已被重载以获取一个保存点参数
代码示例10-2将一行插入到表中,设置保存点svpt1,然后插入第二行。 当事务稍后回滚到svpt1时,第二个插入被撤消,但是第一个插入保持不变。 换句话说,当交易被提交时,只有包含“FIRST”的行将被添加到TAB1
conn.createStatement();
int rows = stmt.executeUpdate("INSERT INTO TAB1 (COL1) VALUES " +
"(’FIRST’)");
// set savepoint
Savepoint svpt1 = conn.setSavepoint("SAVEPOINT_1");
rows = stmt.executeUpdate("INSERT INTO TAB1 (COL1) " +
"VALUES (’SECOND’)");
...
conn.rollback(svpt1);
...
conn.commit();
10.3.4 释放保存点
Connection.releaseSavepoint方法将Savepoint对象作为参数,并从当前事务中删除它和任何后续的保存点。
一旦保存点被释放,尝试在回滚操作中引用它将导致抛出SQLException。
在事务中创建的任何保存点将自动释放,并在事务提交或整个事务回滚时变为无效。
将事务回滚到保存点将自动释放并使无效的所有保存点之后创建的任何其他保存点无效