事务指逻辑上的一组操作,组成这组操作的各个单元,要不全部成功,要不全部不成功。
数据库默认事务是自动提交的,也就是发一条sql它就执行一条。如果想多条SQL语句放在一个事务中执行,则需要对数据库自动提交的默认事务进行更改。
JDBC中事务的使用:
当Jdbc程序向数据库获得一个Connection对象时,默认情况下这个Connection对象会自动向数据库提交在它上面发送的SQL语句。每条单独的语句都是一个事务。每个语句后都隐含一个commit (默认)。
一个独立SQL操作什么时候算执行完毕,JDBC规范是这样规定的:
对数据操作语言(DML,如insert,update,delete)和数据定义语言(如create,drop),语句一执行完就视为执行完毕。
对select语句,当与它关联的ResultSet对象关闭时,视为执行完毕。
对存储过程或其他返回多个结果的语句,当与它关联的所有ResultSet对象全部关闭,所有update count(update,delete等语句操作影响的行数)和output parameter(存储过程的输出参数)都已经获取之后,视为执行完毕。
若想关闭这种默认提交方式,让多条SQL在一个事务中执行,可使用下列语句:
Connection.setAutoCommit(false);但是在之后还要把事务的提交方式改回到自动提交。
实例:
1 publicvoid startTransaction(){ 2 3 4 5 con = DBCManager.getConnect();//获取连接对象 6 7 8 9 10 11 try { 12 13 //设置事务的提交方式为非自动提交: 14 15 con.setAutoCommit(false); 16 17 //将需要添加事务的代码一同放入try,catch块中 18 19 20 21 //创建执行语句 22 23 String sql ="delete from me where id = 7"; 24 25 String sql1 = "update me set name ='chengong' ,age ='34' where id =4"; 26 27 //分别执行事务 28 29 ps = con.prepareStatement(sql); 30 31 ps.executeUpdate(); 32 33 ps = con.prepareStatement(sql1); 34 35 ps.executeUpdate(); 36 37 38 39 //在try块内添加事务的提交操作,表示操作无异常,提交事务。 40 41 42 43 con.commit(); 44 45 46 47 } catch (SQLException e) { 48 49 try { 50 51 //.在catch块内添加回滚事务,表示操作出现异常,撤销事务: 52 53 con.rollback(); 54 55 } catch (SQLException e1) { 56 57 // TODO Auto-generatedcatch block 58 59 e1.printStackTrace(); 60 61 } 62 63 e.printStackTrace(); 64 65 }finally{ 66 67 try { 68 69 //设置事务提交方式为自动提交: 70 71 con.setAutoCommit(true); 72 73 } catch (SQLException e) { 74 75 // TODO Auto-generatedcatch block 76 77 e.printStackTrace(); 78 79 } 80 81 DBCManager.release(rs, ps, con); 82 83 } 84 85 } 86 87 88 89
(1)原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
(2) 一致性(consistency):事务在完成时,必须是所有的数据都保持一致状态。在相关数据库中,所有规则都必须应用于事务的修改,以保持所有数据的完整性。
(3)隔离性(Isolation)
事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离。
(4)持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响
多个线程开启各自事务操作数据库中数据时,数据库系统要负责隔离操作,以保证各个线程在获取数据时的准确性。
如果不考虑隔离性,事务并发处理可能会引起问题:
脏读(dirty reads)
一个事务读取了另一个未提交的并行事务写的数据。
不可重复读(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定义了五种事务隔离级别:
TRANSACTION_NONE JDBC驱动不支持事务
TRANSACTION_READ_UNCOMMITTED 允许脏读、不可重复读和幻读。
TRANSACTION_READ_COMMITTED 禁止脏读,但允许不可重复读和幻读。
TRANSACTION_REPEATABLE_READ 禁止脏读和不可重复读,单运行幻读。
TRANSACTION_SERIALIZABLE 禁止脏读、不可重复读和幻读。
通常来说,一般的应用都会选择Repeatable read或Read committed作为数据库隔离级别来使用。
mysql默认的数据库隔离级别为:REPEATABLE-READ
如何查询当前数据库的隔离级别?select @@tx_isolation;
如何查询当前数据库的隔离级别?select @@tx_isolation;
如何设置当前数据库的隔离级别?set [global/session] transaction isolation level ...;
~此种方式设置的隔离级别只对当前连接起作用。
set transaction isolation level read uncommitted;
set session transaction isolation level read uncommitted;
~此种方式设置的隔离级别是设置数据库默认的隔离级别
set global transaction isolation level read uncommitted;
共享锁:共享锁和共享锁可以共存。共享锁和排他锁不能共存。在Serializable隔离级别下一个事务进行查询操作将会加上共享锁。
排他锁:排他锁和所有锁都不能共存。无论什么隔离级别执行增删改操作时,会加上排他锁
悲观锁:悲观的认为大部分情况下进行操作都会出现更新丢失问题。
在每次进行查询的时候,都手动的加上一个排他锁。
select * from table lock in share mode(读锁、共享锁)
select * from table for update (写锁、排它锁)
乐观锁:乐观的认为大部分的情况下都不会有更新丢失问题。通过时间戳字段,
在表中设计一个版本字段version,当每次对数据库中的数据进行修改操作时,版本号都要进行增加。
如果我的程序修改比较少查询比较多:乐观锁
如果我的程序查询比较少修改比较多:悲观锁
保存点(SavePoint)
JDBC定义了SavePoint接口,提供在一个更细粒度的事务控制机制。当设置了一个保存点后,可以rollback到该保存点处的状态,而不是rollback整个事务。
Connection接口的setSavepoint和releaseSavepoint方法可以设置和释放保存点。