您的位置:首页 > 编程语言 > Java开发

Java的JDBC事务详解

2014-05-01 23:15 239 查看
事务的特性:

1) 原子性(atomicity):事务是数据库的逻辑工作单位,而且是必须是原子工作单位,对于其数据修改,要么全部执行,要么全部不执行。

2) 一致性(consistency):事务在完成时,必须是所有的数据都保持一致状态。在相关数据库中,所有规则都必须应用于事务的修改,以保持所有数据的完整性。

3) 隔离性(isolation):一个事务的执行不能被其他事务所影响。

4) 持久性(durability):一个事务一旦提交,事物的操作便永久性的保存在DB中。即使此时再执行回滚操作也不能撤消所做的更改。

事务(Transaction):是并发控制的单元,是用户定义的一个操作序列。这些操作要么都做,要么都不做,是一个不可分割的工作单位。通过事务,sql server 能将逻辑相关的一组操作绑定在一起,以便服务器 保持数据的完整性。事务通常是以begin transaction开始,以commit或rollback结束。Commint表示提交,即提交事务的所有操作。具体地说就是将事务中所有对数据的更新写回到磁盘上的物理数据库中去,事务正常结束。Rollback表示回滚,即在事务运行的过程中发生了某种故障,事务不能继续进行,系统将事务中对数据库的所有已完成的操作全部撤消,滚回到事务开始的状态。

自动提交事务:每条单独的语句都是一个事务。每个语句后都隐含一个commit。 (默认)

显式事务:以begin transaction显示开始,以commit或rollback结束。

隐式事务:当连接以隐式事务模式进行操作时,sql server数据库引擎实例将在提交或回滚当前事务后自动启动新事务。无须描述事物的开始,只需提交或回滚每个事务。但每个事务仍以commit或rollback显式结束。连接将隐性事务模式设置为打开之后,当数据库引擎实例首次执行下列任何语句时,都会自动启动一个隐式事务:alter table,insert,create,open ,delete,revoke ,drop,select, fetch ,truncate table,grant,update在发出commit或rollback语句之前,该事务将一直保持有效。在第一个事务被提交或回滚之后,下次当连接执行以上任何语句时,数据库引擎实例都将自动启动一个新事务。该实例将不断地生成隐性事务链,直到隐性事务模式关闭为止。

 

Java JDBC事务机制

  首先,我们来看看现有JDBC操作会给我们打来什么重大问题,比如有一个业务:当我们修改一个信息后再去查询这个信息,看是这是一个简单的业务,实现起来也非常容易,但当这个业务放在多线程高并发的平台下,问题自然就出现了,比如当我们执行了一个修改后,在执行查询之前有一个线程也执行了修改语句,这是我们再执行查询,看到的信息就有可能与我们修改的不同,为了解决这一问题,我们必须引入JDBC事务机制,其实代码实现上很简单,一下给出一个原理实现例子供大家参考:

private Connection conn = null;  

private PreparedStatement ps = null;  

 

try {  

    conn.setAutoCommit(false);  //将自动提交设置为false  

              

    ps.executeUpdate("修改SQL"); //执行修改操作  

    ps.executeQuery("查询SQL");  //执行查询操作                 

    conn.commit();      //当两个操作成功后手动提交  

              

} catch (Exception e) {  

    conn.rollback();    //一旦其中一个操作出错都将回滚,使两个操作都不成功  

    e.printStackTrace();  



与事务相关的理论
1.事务(Transaction)的四个属性(ACID)

原子性(Atomic) 对数据的修改要么全部执行,要么全部不执行。

一致性(Consistent) 在事务执行前后,数据状态保持一致性。

隔离性(Isolated) 一个事务的处理不能影响另一个事务的处理。

持续性(Durable) 事务处理结束,其效果在数据库中持久化。

2.事务并发处理可能引起的问题

脏读(dirty read) 一个事务读取了另一个事务尚未提交的数据,

不可重复读(non-repeatable read) 一个事务的操作导致另一个事务前后两次读取到不同的数据

幻读(phantom read) 一个事务的操作导致另一个事务前后两次查询的结果数据量不同。

举例:

事务A、B并发执行时,

当A事务update后,B事务select读取到A尚未提交的数据,此时A事务rollback,则B读到的数据是无效的"脏"数据。

当B事务select读取数据后,A事务update操作更改B事务select到的数据,此时B事务再次读去该数据,发现前后两次的数据不一样。

当B事务select读取数据后,A事务insert或delete了一条满足A事务的select条件的记录,此时B事务再次select,发现查询到前次不存在的记录("幻影"),或者前次的某个记录不见了。

JDBC的事务支持

JDBC对事务的支持体现在三个方面:
1.自动提交模式(Auto-commit mode)

Connection提供了一个auto-commit的属性来指定事务何时结束。

a.当auto-commit为true时,当每个独立SQL操作的执行完毕,事务立即自动提交,也就是说每个SQL操作都是一个事务。

一个独立SQL操作什么时候算执行完毕,JDBC规范是这样规定的:

对数据操作语言(DML,如insert,update,delete)和数据定义语言(如create,drop),语句一执行完就视为执行完毕。

对select语句,当与它关联的ResultSet对象关闭时,视为执行完毕。

对存储过程或其他返回多个结果的语句,当与它关联的所有ResultSet对象全部关闭,所有update count(update,delete等语句操作影响的行数)和output parameter(存储过程的输出参数)都已经获取之后,视为执行完毕。

b. 当auto-commit为false时,每个事务都必须显示调用commit方法进行提交,或者显示调用rollback方法进行回滚。auto-commit默认为true。

JDBC提供了5种不同的事务隔离级别,在Connection中进行了定义。

2.事务隔离级别(Transaction Isolation Levels)

JDBC定义了五种事务隔离级别:

TRANSACTION_NONE JDBC驱动不支持事务

TRANSACTION_READ_UNCOMMITTED 允许脏读、不可重复读和幻读。

TRANSACTION_READ_COMMITTED 禁止脏读,但允许不可重复读和幻读。

TRANSACTION_REPEATABLE_READ 禁止脏读和不可重复读,单运行幻读。

TRANSACTION_SERIALIZABLE 禁止脏读、不可重复读和幻读。

JDBC事务级别分为5级:

(1)TRANSACTION_NONE

正式地讲,TRANSACTION_NONE不是一个有效的事务级别。根据java.sql.ConnectionAPI文件,这个级别表示事务是不被支持的,因此理论上说你不能使用TRANSACTION_NONE作为一个自变量赋给Connection.setTransactionIsolation()方法。

事实上,虽然一些数据库实施了这个事务级别,但是Oracle9i却没有实施。

(2)TRANSACTION_READ_UNCOMMITTED

这是最快的完全有效的事务级别。它允许你读取其他还没有被提交到数据库的并发事务做出的修改。这个API文件指出,脏读取(dirtyreads)、不可重复读取(non-repeatable reads)和错误读取(phantomreads)都可以在这个事务级别发生(参阅"一些非ACID事务问题"部分)。这个级别意在支持ACID的"原子性(Atomic)"部分,在这个级别中,你的修改如果被提交,将被认为是同时发生的;如果被撤销,就被当作什么也没发生。

Oracle9i不支持这个级别。

(3)TRANSACTION_READ_COMMITTED

这是继TRANSACTION_READ_UNCOMMITTED之后最快的完全有效的级别。在此级别中,你可以读取已经被提交到数据库中的其他并发事务所做出的修改。API文件指出,脏读取在这个级别中是被禁止的,但是不可重复读取和错误读取都可以发生。

这个级别是Oracle9i默认的级别。

(4)TRANSACTION_REPEATABLE_READ

这个级别比TRANSACTION_SERIALIZABLE快,但是比其他的事务级别要慢。读取操作可以重复进行,这意味着两次读取同样的域应该总是得到同样的值,除非事务本身改变了这个值。API文件指出,脏读取和不可重复读取在这个事务级别中是被禁止的,但是错误读取可以发生。

从技术上讲,数据库通过在被读取或写入的行上加锁来实施这个级别,并且保持锁定状态直到事务结束。这就防止了这些行被修改或删除,但是不能防止额外的行被添加--因此,就可能产生错误读取。?????

Oracle9i不支持这个级别。

(5)TRANSACTION_SERIALIZABLE

这是最慢的事务级别,但是它完全与ACID兼容。"单词可串行化(serializable)"指的就是ACID兼容,其中你的事务被认为在整体上已经发生,就如同其他所有已提交的事务在这个事务之前或之后全部发生。换句话说,事务被串行执行。脏读取、不可重复读取和错误读取在TRANSACTION_SERIALIZABLE级别是全部被禁止的。

从技术上讲,数据库通过锁定在事务中使用的表来实施这个级别。???????

Oracle9i支持这个级别(正如每个与符合ACID的数据库那样)。

 

Oracl事务级别

Oracle支持的2种事务隔离级别:Readcommitted,Serializable;

IsolationLevel
Description
Readcommitted
This is thedefault transaction isolation level. Each query executed by atransaction sees only data that was committed before the query (notthe transaction) began. An Oracle query never reads dirty(uncommitted) data.
BecauseOracle does not prevent other transactions from modifying the dataread by a query, that data can be changed by other transactionsbetween two executions of the query. Thus, a transaction that runsa given query
twice can experience both nonrepeatable read andphantoms.
默认的隔离级别设置。事务中的查询只能看到在此查询之前(而非事务开始之前)提交的数据。
由于Oracle不会因为查询数据而阻止另外一个事务修改数据,因此数据可以在一个事务中的2 次查询中,查到不同的结果。因此可能出现nonrepeatable read and phantoms 的情况。
Serializable
Serializabletransactions see only those changes that were committed at the timethe transaction began, plus those changes made by the transactionitself through INSERT , UPDATE , and DELETE statements.Serializable
transactions do not experience nonrepeatable reads orphantoms.
Serializabletransactions只能查到此事务开始之前提交的数据,以及本次事务内部的INSERT,UPDATE,DELETE语句修改后的数据。因此能够做到没有nonrepeatablereads or phantoms的情况。 
以上 2点都会保证不能脏读脏写,就是说不能得到另外一个事务修改没有提交的事务的修改后的数据。区别在于前者做不到nonrepeatablereads or phantoms,后者可以做到。

举例如下:
前提条件:

有四个事务:事务A、事务B、事务C、事务D;

事务A:1个UPDATE操作;

事务B:3个操作,依次为SELECT-1、UPDATE、SELECT-2;

事务C:1个UPDATE操作;

事务D:1个UPDATE操作;

从时间前后来讲,事件顺序为:事务A的开始、事务A的UPDATE、事务B的开始、事务C的开始、事务D的开始、事务D的UPDATE、事务A的提交、事务B的SELECT-1、事务C的UPDATE、事务C的提交、事务B的UPDATE、事务B的SELECT-2、事务B的提交、事务D的提交;

结论:

如果事务B的事务隔离级别为Readcommitted

(1)事务B的SELECT-1能读到事务A的UPDATE后的数据吗?

可以。因为在SELECT-1之前事务A已经提交了。

(2)事务B的SELECT-1能读到事务B的UPDATE后的数据吗?

不能。虽然在同一个事务内,但是事务B的UPDATE是在SELECT-1之后。

(3)事务B的SELECT-1能读到事务C的UPDATE后的数据吗?

不能。因为这个时候这个时候事务C的UPDATE还没有进行。

(4)事务B的SELECT-1能读到事务D的UPDATE后的数据吗?

不能。因为事务D的UPDATE这个时候还没有提交。Readcommited不会脏读。(如果读到了,即为脏读。)

(5)事务B的SELECT-2能读到事务A的UPDATE后的数据吗?

可以。因为在SELECT-2之前事务A已经提交了。

(6)事务B的SELECT-2能读到事务B的UPDATE后的数据吗?

可以。因为在同一个事务内部,而且UPDATE在前,SELECT-2在后。

(7)事务B的SELECT-2能读到事务C的UPDATE后的数据吗?

可以。因为Readcommited会读到已经提交的事务内的修改,而事务C这个时候已经提交了。

(8)事务B的SELECT-2能读到事务D的UPDATE后的数据吗?

不能。因为事务D的UPDATE这个时候还没有提交。Readcommited不会脏读。(如果读到了,即为脏读。)

如果事务B的事务隔离级别为Serializable

(1)事务B的SELECT-1能读到事务A的UPDATE后的数据吗?

不能。因为在事务B开始的时候事务A还没有提交。Serializable只能查到此事务开始之前提交的数据。

(2)事务B的SELECT-1能读到事务B的UPDATE后的数据吗?

不能。虽然在同一个事务内,但是事务B的UPDATE是在SELECT-1之后。

(3)事务B的SELECT-1能读到事务C的UPDATE后的数据吗?

不能。因为在事务B开始的时候事务C还没有开始。而且,事务C的UPDATE发生在事务B的SELECT-1之后。

(4)事务B的SELECT-1能读到事务D的UPDATE后的数据吗?

不能。因为事务D的UPDATE这个时候还没有提交。Serializable不会脏读。(如果读到了,即为脏读。)

(5)事务B的SELECT-2能读到事务A的UPDATE后的数据吗?

不能。因为在事务B开始的时候事务A还没有提交。Serializable只能查到此事务开始之前提交的数据。

(6)事务B的SELECT-2能读到事务B的UPDATE后的数据吗?

可以。Serializable可以查到本事物内部修改的数据。

(7)事务B的SELECT-2能读到事务C的UPDATE后的数据吗?

不能。因为在事务B开始的时候事务C还没有开始,更别说提交了。Serializable只能查到此事务开始之前提交的数据。

(8)事务B的SELECT-2能读到事务D的UPDATE后的数据吗?

不能。因为事务D的UPDATE这个时候还没有提交。Serializable不会脏读。(如果读到了,即为脏读。)

3.保存点(SavePoint)

JDBC定义了SavePoint接口,提供在一个更细粒度的事务控制机制。当设置了一个保存点后,可以rollback到该保存点处的状态,而不是rollback整个事务。

Connection接口的setSavepoint和releaseSavepoint方法可以设置和释放保存点。

JDBC规范虽然定义了事务的以上支持行为,但是各个JDBC驱动,数据库厂商对事务的支持程度可能各不相同。如果在程序中任意设置,可能得不到想要的效果。为此,JDBC提供了DatabaseMetaData接口,提供了一系列JDBC特性支持情况的获取方法。比如,通过DatabaseMetaData.supportsTransactionIsolationLevel方法可以判断对事务隔离级别的支持情况,通过DatabaseMetaData.supportsSavepoints方法可以判断对保存点的支持情况。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: