锁的用处及脏读、不可重复读和幻觉读的概念 选择自 lanbaibai 的 Blog
关键字 锁的用处及脏读、不可重复读和幻觉读的概念
出处
锁就是防止其他事务访问指定的资源的手段。锁是实现并发控制的主要方法,是多个用户能够同时操纵同一个数据库中的数据而不发生数据不一致现象的重要保障。一般来说,锁可以防止脏读、不可重复读和幻觉读。脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是脏数据,依据脏数据所做的操作可能是不正确的。不可重复读是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。幻觉读是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。
前一段时间看了网友的文章介绍,现加入以下隔离的概念, 在这里感谢jeex的帮助
ANSI SQL中定义的4个隔离级实际上是用对锁的操作来定义的:
脏读: 读数据时不加锁。
提交读: 在读数据之前加一个读锁,读完之后释放锁。
可重复读: 在读数据之前加一个读锁,读完之后不释放锁,直到事务rollback或者commit后才释放锁。
串行化读: 在读数据之前在读取的条件上加锁(称为条件锁),读完之后不释放锁,直到事务rollback或者commit后才释放锁。
作者Blog:http://blog.csdn.net/lanbaibai/
java语言的隔离级别:(JDBC中的)
以下说明了隔离级别:
static int TRANSACTION_NONE
指示事务不受支持的常量。
static int TRANSACTION_READ_COMMITTED
指示防止发生脏读的常量;不可重复读和虚读有可能发生。
static int TRANSACTION_READ_UNCOMMITTED
指示可以发生脏读 (dirty read)、不可重复读和虚读 (phantom read) 的常量。
static int TRANSACTION_REPEATABLE_READ
指示防止发生脏读和不可重复读的常量;虚读有可能发生。
static int TRANSACTION_SERIALIZABLE
指示防止发生脏读、不可重复读和虚读的常量。
TRANSACTION_NONE
static final int TRANSACTION_NONE指示事务不受支持的常量。
--------------------------------------------------------------------------------
TRANSACTION_READ_UNCOMMITTED
static final int TRANSACTION_READ_UNCOMMITTED指示可以发生脏读 (dirty read)、不可重复读和虚读 (phantom read) 的常量。此级别允许由某一事务更改的行在已提交该行中的所有更改之前被另一个事务读取(“脏读”)。如果所有更改都被回滚,则第二个事务将检索无效的行。
-------------------------------------------------------------------------------
TRANSACTION_READ_COMMITTED
static final int TRANSACTION_READ_COMMITTED指示防止发生脏读的常量;不可重复读和虚读有可能发生。此级别只禁止事务读取其中带有未提交更改的行。
--------------------------------------------------------------------------------
TRANSACTION_REPEATABLE_READ
static final int TRANSACTION_REPEATABLE_READ指示防止发生脏读和不可重复读的常量;虚读有可能发生。此级别禁止事务读取其中带有未提交更改的行,它还禁止这种情况:一个事务读取某一行,而另一个事务更改该行,第一个事务重新读取该行,并在第二次读取时获得不同的值(“不可重复读”)。
--------------------------------------------------------------------------------
TRANSACTION_SERIALIZABLE
static final int TRANSACTION_SERIALIZABLE指示防止发生脏读、不可重复读和虚读的常量。此级别包括 TRANSACTION_REPEATABLE_READ 中禁止的事项并进一步禁止出现这种情况:某一事务读取所有满足 WHERE 条件的行,另一个事务插入一个满足 WHERE 条件的行,第一个事务重新读取满足相同条件的行,并在第二次读取时检索到额外的“虚”行。
为了在性能与一致性之间寻求平衡才出现了上面的几种级别。事务保护的级别越高,性能损失就越大。
假定您的数据库和 JDBC 驱动程序支持这个特性,则给定一个 Connection 对象,您可以明确地设置想要的事务级别:
conn.setTransactionLevel(TRANSACTION_SERIALIZABLE) ;
可以通过下面的方法确定当前事务的级别:
int level = conn.getTransactionIsolation();
if(level == Connection.TRANSACTION_NONE)
System.out.println("TRANSACTION_NONE");
else if(level == Connection.TRANSACTION_READ_UNCOMMITTED)
System.out.println("TRANSACTION_READ_UNCOMMITTED");
else if(level == Connection.TRANSACTION_READ_COMMITTED)
System.out.println("TRANSACTION_READ_COMMITTED");
else if(level == Connection.TRANSACTION_REPEATABLE_READ)
System.out.println("TRANSACTION_REPEATABLE_READ");
else if(level == Connection.TRANSACTION_SERIALIZABLE)
System.out.println("TRANSACTION_SERIALIZABLE");
public interface Connection
与特定数据库的连接(会话)。在连接上下文中执行 SQL 语句并返回结果。
Connection 对象的数据库能够提供信息描述其表、所支持的 SQL 语法、存储过程和此连接的功能等。此信息是使用 getMetaData 方法获得的。
注:默认情况下,Connection 对象处于自动提交模式下,这意味着它在执行每个语句后都会自动提交更改。如果禁用自动提交模式,为了提交更改,必须显式调用 commit 方法;否则无法保存数据库更改。
使用 JDBC 2.1 核心 API 创建的 Connection 对象有一个与之关联的最初为空的类型映射表。用户可以为此类型映射表中的 UDT 输入一个自定义映射关系。在使用 ResultSet.getObject 方法从数据源中检索 UDT 时,getObject 方法将检查该连接的类型映射表,以查看是否有对应该 UDT 的项。如果有,那么 getObject 方法会将该 UDT 映射到所指示的类。如果没有项,则会使用标准映射关系映射该 UDT。
用户可以创建一个新的类型映射表,该映射表是一个 java.util.Map 对象,可在其中创建一个项,并将该项传递给可以执行自定义映射关系的 java.sql 方法。在这种情况下,该方法将使用给定的类型映射表,而不是与连接相关联的映射表。
例如,以下代码片段指定 SQL 类型 ATHLETES 将被映射到 Java 编程语言中的 Athletes 类。该代码片段为 Connection 对象 con 获取类型映射表,并在其中插入一些项,然后使用新的项将该类型映射表设置为连接的类型映射表。
java.util.Map map = con.getTypeMap();
map.put("mySchemaName.ATHLETES", Class.forName("Athletes"));
con.setTypeMap(map);
public interface Statement
用于执行静态 SQL 语句并返回它所生成结果的对象。
在默认情况下,同一时间每个 Statement 对象在只能打开一个 ResultSet 对象。因此,如果读取一个 ResultSet 对象与读取另一个交叉,则这两个对象必须是由不同的 Statement 对象生成的。如果存在某个语句的打开的当前 ResultSet 对象,则 Statement 接口中的所有执行方法都会隐式关闭它。
public interface ResultSet
表示数据库结果集的数据表,通常通过执行查询数据库的语句生成。
ResultSet 对象具有指向其当前数据行的指针。最初,指针被置于第一行之前。next 方法将指针移动到下一行;因为该方法在 ResultSet 对象中没有下一行时返回 false,所以可以在 while 循环中使用它来迭代结果集。
默认的 ResultSet 对象不可更新,仅有一个向前移动的指针。因此,只能迭代它一次,并且只能按从第一行到最后一行的顺序进行。可以生成可滚动和/或可更新的 ResultSet 对象。以下代码片段(其中 con 为有效的 Connection 对象)演示了如何生成可滚动且不受其他更新影响的、可更新的结果集。请参阅 ResultSet 字段以了解其他选项。
Statement stmt = con.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE);
ResultSet rs = stmt.executeQuery("SELECT a, b FROM TABLE2");
// rs will be scrollable, will not show changes made by others,
// and will be updatable
ResultSet 接口提供用于从当前行检索列值的获取方法(getBoolean、getLong 等)。可以使用列的索引编号或列的名称检索值。一般情况下,使用列索引较为高效。列从 1 开始编号。为了获得最大的可移植性,应该按从左到右的顺序读取每行中的结果集列,而且每列只能读取一次。
对于获取方法,JDBC 驱动程序尝试将基础数据转换为在获取方法中指定的 Java 类型,并返回适当的 Java 值。JDBC 规范有一个表,显示允许的从 SQL 类型到供 ResultSet 获取方法使用的 Java 类型的映射关系。
用作获取方法的输入的列名称不区分大小写。用列名称调用获取方法时,如果多个列具有这一名称,则返回第一个匹配列的值。列名称选项在生成结果集的 SQL 查询中使用列名称时使用。对于没有在查询中显式命名的列,最好使用列编号。如果使用列名称,程序员无法保证名称实际所指的就是预期的列。
在 JDBC 2.0 API (JDK 1.2) 中,此接口添加了一组更新方法。关于获取方法参数的注释同样适用于更新方法的参数。
可以用以下两种方式使用更新方法: ------这不就是游标 吗??
更新当前行中的列值。在可滚动的 ResultSet 对象中,可以向前和向后移动指针,将其置于绝对位置或相对于当前行的位置。以下代码片段更新 ResultSet 对象 rs 的第五行中的 NAME 列,然后使用方法 updateRow 更新用于派生 rs 的数据源表。
rs.absolute(5); // moves the cursor to the fifth row of rs
rs.updateString("NAME", "AINSWORTH"); // updates the
// NAME column of row 5 to be AINSWORTH
rs.updateRow(); // updates the row in the data source
将列值插入到插入行中。可更新的 ResultSet 对象具有一个与其关联的特殊行,该行用作构建要插入的行的暂存区域 (staging area)。以下代码片段将指针移动到插入行,构建一个三列的行,并使用方法 insertRow 将其插入到 rs 和数据源表中。
rs.moveToInsertRow(); // moves cursor to the insert row
rs.updateString(1, "AINSWORTH"); // updates the
// first column of the insert row to be AINSWORTH
rs.updateInt(2,35); // updates the second column to be 35
rs.updateBoolean(3, true); // updates the third column to true
rs.insertRow();
rs.moveToCurrentRow();
当生成 ResultSet 对象的 Statement 对象关闭、重新执行或用来从多个结果的序列检索下一个结果时,ResultSet 对象会自动关闭。
ResultSet 对象的列的编号、类型和属性由 ResultSet.getMetaData 方法返回的 ResulSetMetaData 对象提供。