在SQL术语中,事务是逻辑工作单元(logical unit of work,LUW)构成的一个或多个语句。这在某种含义上意味着,一切都是事务。不过,通常而言,术语事务用来表示或全或无的系列操作;也就是说,要么一切十分成功,要么什么也没有发生。典型的事务是从银行帐户提款,并存放到另一个。只要提款完成,金额就消失了。另一个范例是复式薄记记帐法中的借方和贷方:借方和贷方都必须完成。
在单用户模式,事务非常容易理解-它们只是和保存或忘记应用程序的状态有关。在多用户模式中,事务变得复杂多了。多用户事务的经典说明是银行帐户,其中一个应用程序试图在借记帐户,同时另一个应用程序试图贷记同一个帐户。在前面已经介绍过多线程编程(并发编程)。类似的,事务的根本问题是保持两个事务相互隔离,否则一个应用程序就可能影响另一个,从而导致错误的程序状态。当处理多个访问相同数据的用户时,通常可能出现三种问题:
n 脏读:当应用程序使用了被另一个应用程序修改过的数据,而这个数据处于未提交状态时,就会发生脏读。第二个应用程序随后会请求回滚被其修改的数据。第一个事务使用的数据就会被损坏,或者"变脏"。
n 不可重复的读:当一个事务获得了数据,而该数据随后被一个单独的事务所更改时,若第一个事务再次读取更改后的数据,就会发生不可重复的读。这样,第一个事务进行了一个不可重复的读。
n 虚读:当事务通过某种查询获取了数据,另一个事务修改了部分该数据,原来的事务第二次获取该数据时,就会发生虚读。第一个事务现在会有不同的结果集,它可能包含虚读。
为了解决与"多个线程请求相同数据"相关的问题,事务之间用锁相互隔开。多数主流的数据库支持不同类型的锁;因此,JDBC API支持不同类型的事务,它们由Connection对象指派或确定。在JDBC API中可以获得下列事务级别:
n TRANSACTION_NONE 说明不支持事务
n TRANSACTION_READ_UNCOMMITTED说明在提交前一个事务可以看到另一个事务的变化。这样脏读,不可重复的读和虚读都是允许的。
n TRANSACTION_READ_COMMITTED说明读取未提交的数据是不允许的。这个级别仍然允许不可重复的读和虚读产生。
n TRANSACTION_REPEATABLE_READ说明事务保证能够再次读取相同的数据而不会失败,但虚读仍然会出现。
n TRANSACTION_SERIALIZABLE是最高的事务级别,它防止脏读,不可重复读和虚读。
为什么不是所有事务都运行在TRANSACTION_SERIALIZABLE模式以保证最高程度的数据完整性呢?问题在于,和处理多线程编程有关的问题相似,事务保护的级别越高,性能损失就越大。假定您的数据库和JDBC驱动程序支持这个特性,则给定一个Connection对象,您可以明确地设置想要的事务级别:con.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
您还可以确定当前事务的级别:
int transactionIsolation = con.getTransactionIsolation();
if (transactionIsolation == Connection.TRANSACTION_NONE)
System.out.println("当前事务级别为:TRANSACTION_NONE。");
代码:
import java.sql.Connection;
import java.sql.SQLException;
import javax.naming.NamingException;
public class TranTest {
public static void main(String[] args) {
String jndiname = "jdbcPool/mydatasource";
Connection con = null;
try {
con = DBCon.getConnectionFromPooledDataSource(jndiname);
int transactionIsolation = con.getTransactionIsolation();
if (transactionIsolation == Connection.TRANSACTION_NONE) {
System.out.println("当前事务级别为:TRANSACTION_NONE。");
} else if (transactionIsolation == Connection.TRANSACTION_READ_COMMITTED) {
System.out.println("当前事务级别为:TRANSACTION_READ_COMMITTED。");
} else if (transactionIsolation == Connection.TRANSACTION_READ_UNCOMMITTED) {
System.out.println("当前事务级别为:TRANSACTION_READ_UNCOMMITTED。");
} else if (transactionIsolation == Connection.TRANSACTION_REPEATABLE_READ) {
System.out.println("当前事务级别为:TRANSACTION_REPEATABLE_READ。");
} else if (transactionIsolation == Connection.TRANSACTION_SERIALIZABLE) {
System.out.println("当前事务级别为:TRANSACTION_SERIALIZABLE。");
}
con.setAutoCommit(false);
con.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
transactionIsolation = con.getTransactionIsolation();
if (transactionIsolation == Connection.TRANSACTION_NONE) {
System.out.println("当前事务级别为:TRANSACTION_NONE。");
} else if (transactionIsolation == Connection.TRANSACTION_READ_COMMITTED) {
System.out.println("当前事务级别为:TRANSACTION_READ_COMMITTED。");
} else if (transactionIsolation == Connection.TRANSACTION_READ_UNCOMMITTED) {
System.out.println("当前事务级别为:TRANSACTION_READ_UNCOMMITTED。");
} else if (transactionIsolation == Connection.TRANSACTION_REPEATABLE_READ) {
System.out.println("当前事务级别为:TRANSACTION_REPEATABLE_READ。");
} else if (transactionIsolation == Connection.TRANSACTION_SERIALIZABLE) {
System.out.println("当前事务级别为:TRANSACTION_SERIALIZABLE。");
}
} catch (NamingException ex) {
System.err.println("Name Not Bound : " + ex.getMessage());
} catch (SQLException ex) {
System.err.println("SQLException : " + ex.getMessage());
} finally {
try {
if (con != null)
con.close();
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("程序执行结束!");
}
}