1.概述:
在jdbc的数据库操作中,一项事务是由一条或是多条表达式所组成的一个不可分割的工作单元。我们通过提交commit()或是回滚rollback()来结束事务的操作。关于事务操作的方法都位于接口java.sql.Connection中。
2.特点:
★ 在jdbc中,事务操作缺省是自动提交。也就是说,一条对数据库的更新表达式代表一项事务操作,操作成功后,系统将自动调用commit()来提交,否则将调用rollback()来回滚。
★ 在jdbc 中,可以通过调用setAutoCommit(false)来禁止自动提交。之后就可以把多个数据库操作的表达式作为一个事务,在操作完成后调用 commit()来进行整体提交,倘若其中一个表达式操作失败,都不会执行到commit(),并且将产生响应的异常;此时就可以在异常捕获时调用 rollback()进行回滚。这样做可以保持多次更新操作后,相关数据的一致性,示例如下:
try {
conn =
DriverManager.getConnection
("jdbc:oracle:thin:@host:1521:SID","username","userpwd";
conn.setAutoCommit(false);//禁止自动提交,设置回滚点
stmt = conn.createStatement();
stmt.executeUpdate(“alter table …”); //数据库更新操作1
stmt.executeUpdate(“insert into table …”); //数据库更新操作2
conn.commit(); //事务提交
}catch(Exception ex) {
ex.printStackTrace();
try {
conn.rollback(); //操作不成功则回滚
}catch(Exception e) {
e.printStackTrace();
}
}
★ jdbc API支持事务对数据库的加锁,并且提供了5种操作支持,2种加锁密度。
5种支持:
static int TRANSACTION_NONE = 0;
→禁止事务操作和加锁。
static int TRANSACTION_READ_UNCOMMITTED = 1;
→允许脏数据读写(dirty reads)、重复读写(repeatable reads)和影象读写(phntom
reads)
static int TRANSACTION_READ_COMMITTED = 2;
→禁止脏数据读写(dirty reads),允许重复读写(repeatable reads)和影象读写(phntom reads)
static int TRANSACTION_REPEATABLE_READ = 4;
→禁止脏数据读写(dirty reads)和重复读写(repeatable reads),允许影象读写(phntom reads)
static int TRANSACTION_SERIALIZABLE = 8;
→禁止脏数据读写(dirty reads)、重复读写(repeatable reads)和允许影象读写(phntom reads)
2种密度:
最后一项为表加锁,其余3~4项为行加锁。
脏数据读写(dirty reads):当一个事务修改了某一数据行的值而未提交时,另一事务读取了此行值。倘若前一事务发生了回滚,则后一事务将得到一个无效的值(脏数据)。
重复读写(repeatable reads):当一个事务在读取某一数据行时,另一事务同时在修改此数据行。则前一事务在重复读取此行时将得到一个不一致的值。
影象读写(phantomreads):当一个事务在某一表中进行数据查询时,另一事务恰好插入了满足了查询条件的数据行。则前一事务在重复读取满足条件的值时,将得到一个额外的“影象“值。
Jdbc根据数据库提供的缺省值来设置事务支持及其加锁,当然,也可以手工设置:
setTransactionIsolation(TRANSACTION_READ_UNCOMMITTED);
可以查看数据库的当前设置:
getTransactionIsolation()
需要注意的是,在进行受动设置时,数据库及其驱动程序必须得支持相应的事务操作操作才行。
上述设置随着值的增加,其事务的独立性增加,更能有效的防止事务操作之间的冲突;同时也增加了加锁的开销,降低了用户之间访问数据库的并发性,程序的运行效率也回随之降低。因此得平衡程序运行效率和数据一致性之间的冲突。一般来说,对于只涉及到数据库的查询操作时,可以采用TRANSACTION_READ_UNCOMMITTED 方式;对于数据查询远多于更新的操作,可以采用TRANSACTION_READ_COMMITTED方式;对于更新操作较多的,可以采用 TRANSACTION_REPEATABLE_READ;在数据一致性要求更高的场合再考虑最后一项,由于涉及到表加锁,因此会对程序运行效率产生较大的影响。
另外,在oracle中数据库驱动对事务处理的缺省值是TRANSACTION_NONE,即不支持事务操作,所以需要在程序中手动进行设置。
3.小结
jdbc提供的对数据库事务操作的支持是比较完整的,通过事务操作可以提高程序的运行效率,保持数据的一致性。
提示4:正确地使用事务
在创建可靠的系统时,正确地使用事务是非常关键的:如果登记一项销售额涉及到改变三个独立的表,那么数据库应该在这三个表中反映出这些变化或者所有表中都不反映。在Oracle中,通过执行一系列SQL 语句来处理事务,然后把它们“提交”到数据库,或者“回滚”到事务开始。高效的事务设计通常是系统成功部署的关键。
核心的JDBC包支持4种“transaction isolation modes(事务隔离模式)”,允许程序指定它们希望的事务行为。Oracle支持它们中的两种:默认的 “read committed(读提交数据)” 和更安全的“serializable(可串行化)”模式。“read committed” 模式支持不可重复读(non-repeatable read),在这种情况下,一个事务所做的修改对查询中的另一个事务是可见的;还支持错误读取(phantom read),这种情况下,被其他事务所做的修改对于运行的查询是可见的。依赖于在多个操作中数据库的完全一致视图的应用程序应该选择损失一定的性能,使用更严格的“serializable(可串行化)”设置。
JDBC连接在默认的情况下也在“auto-commit”(自动提交)模式下运行,这意味着每个语句一执行完就立即提交给数据库后。这种模式有一些优点,开发人员不需要在每次更新或插入操作之后调用commit() 方法。它还可以确保最新的数据库视图。另一方面,关闭自动提交将可以稍微提高一点性能,并且将允许多语句事务。
在servlet环境中使用JDBC事务时,与使用连接共享时应用了许多相同的规则。在代码的结尾处或者回滚或者提交事务是非常重要的。确保这一点的最好方法是使用try...catch...finally 块。不完整的事务可能产生不可预见的后果,包括从不完整或不一致的数据库视图到当事务一个接一个排队等候访问被锁定的资源时使整个应用程序被锁定的种种情况。
下面的例子说明如何使用JDBC事务:
Connection con = null;
try {
ds = (DataSource)myContext.lookup("jdbc/oracleServer");
pooledCon = ds.getConnection("scott", "tiger");
pooledCon.setAutoCommit(false);
pooledCon.setTransactionIsolation(
Connection.TRANSACTION_SERIALIZABLE);
// ..
pooledCon.commit();
} catch (Exception ignored) {
try { pooledCon.rollback(); } catch (SQLException ig) {}
} finally {
if(pooledCon != null) {
pooledCon.setAutoCommit(true);
pooledCon.close();
}
}
连接池中得到的每一个连接是极为重要的。
尽管常规事务很有吸引力,但无论你使用哪种连接管理模型,都绝不应该将它们推广应用到对服务器的多个请求(一个或更多servlet的多个调用)。用户决不应该在一个事务没有完成时就发出第二个请求。需要跨多个对象或多个请求中分布事务的应用程序应该考虑其他可选的体系结构。举例来说,同Java事务处理API (JTA)一起的JDBC 2.0可选包,使与完善的事务处理服务器的集成成为可能。
如果你正在运行Oracle9i第2版,你将可以获取JDBC 3.0中引入的一些新的事务特性。这些特性中最重要的是事务存储点(Transaction Savepoint),它允许你将事务回滚到某一特定点,而不是要么全部回滚、要么全不回滚。
public void createA(Connection conn,A a){
int flag=0;
if(conn==null){
flag=1;
conn = getConnection();
}
try{
{do update}
if(flag==1){
conn.commit();
}
} catch(Exception e){
if(flag==1){
conn.rollback();
}
throw e;
} finally{
if(flag==1){
conn.close();
}
}
}
public void createB(B b){
Connection conn = getConnection();
{create b}
try{
createA(conn,a);
conn.commit();
} catch(Exception e){
conn.rollback();
}