事务的特性
事务是由一组SQL语句组成的逻辑工作单元,这些语句要么全部执行,要么不执行,只要有一条SQL语句执行失败,则已执行的SQL语句会回滚到执行之前的状态,这样就保证了数据库数据的一致性,不产生混乱的数据信息。
通过事务,可以实现数据的共享,事务可以保证数据的一致性。只有COMMIT后,其他用户才可以看到数据更改。
事务处理的原理:
(1)在Oracle中,sql语句更改数据时,把旧值暂时写入一个叫回滚段(ROLLBACK SEGMENT)的空间中。
(2)COMMIT时,清除回滚段的旧值。
可撤消的语句: 适用于所有dml语句。
一组SQL语句操作要成为事务,数据库管理系统必须保证这组操作的原子性(Atomicity)、一致性(consistency、隔离性(Isolation)和持久性(Durability),这就是ACDI特性。
原子性是指事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败。比如在同一个事务中的SQL语句,要么全部执行成功,要么全部执行失败。
一致性:事务必须使数据库从一个一致性状态变换到另外一个一致性状态。举例说明:张三向李四转100元,转账前和转账后的数据是正确的状态,这就叫一致性,如果出现张三转出100元,李四账号没有增加100元这就出现了数据错误,就没有达到一致性。
隔离性:事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
持久性:持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。例如在使用JDBC操作数据库时,在提交事务方法后,提示用户事务操作完成,当程序执行完成直到看到提示后,就可以认定事务正确提交,即使这时候数据库出现了问题,也必须要将事务完全执行完成,否则就会看到提示事务处理完毕,但是数据库因为故障而没有执行事务的重大错误。
【例4-7】建立账号表account_money,在该表上建立约束,账户金额必须大于零。
写一段匿名块,从张三的账户1转账600元给账户2,查看转账是否成功。
具体代码如下:
–第四章\zsgc.sql
–建立账号表
create table account_money
(
id number(4) not null,
name varchar2(4) not null,
money number(5,2) not null
)
;
– 增加一个检查约束
alter table account_money
add constraint CK_money
check (money>=0);
–向张三账号增加数据
insert into ACCOUNT_MONEY (ID, NAME, MONEY)
values (1001, ‘张三’, 500.00);
insert into ACCOUNT_MONEY (ID, NAME, MONEY)
values (1002, ‘张三’, 1.00);
BEGIN
–从张三的1001账户转入张三的1002账户
UPDATE account_money a SET a.Money=a.Money-600 WHERE a.Id=‘1001’;
UPDATE account_money a SET a.Money=a.Money+600 WHERE a.Id=‘1002’;
COMMIT;–提交事务
EXCEPTION --异常处理
WHEN OTHERS THEN ROLLBACK;–出现异常就回滚
Dbms_Output.Put_Line(‘转账异常,转账失败’);
end;
Select * from account_money;
执行后,如图4-16所示。
图4-16查询表数据
在程序中提示转账失败,如图4-17所示。
图4-17转账失败
在上述代码中,因为账户设置了检查约束,当账户小于0时,就会出现异常。如果不进行事务异常处理,那么第二条更新语句会被执行。当做了事务异常处理后,当出现异常就会回滚。
【例4-8】写一个匿名块,查询出表account_money转账前的两个账户余额。然后从账户1001转账100元给账户1002,查询转账后的两个账户余额。如果转账失败,提示错信息并显示两个账户的余额。
具体代码如下:
–第四章\zsgc.sql
DECLARE
account_a account_money.Money%TYPE;
account_b account_money.Money%TYPE;
BEGIN
SELECT a.money INTO account_a FROM account_money a WHERE a.Id=‘1001’;
SELECT a.money INTO account_b FROM account_money a WHERE a.Id=‘1002’;
Dbms_Output.Put_Line(‘转账前A账户余额:’||account_a);
Dbms_Output.Put_Line(‘转账前B账户余额:’||account_b);
Dbms_Output.Put_Line(‘转账前总余额:’||(account_a+account_b));
UPDATE account_money SET money=money-100 WHERE ID=‘1001’;
UPDATE account_money SET money=money+100 WHERE ID=‘1002’;
COMMIT;
SELECT a.money INTO account_a FROM account_money a WHERE a.Id=‘1001’;
SELECT a.money INTO account_b FROM account_money a WHERE a.Id=‘1002’;
Dbms_Output.Put_Line(‘转账后A账户余额:’||account_a);
Dbms_Output.Put_Line(‘转账后B账户余额:’||account_b);
Dbms_Output.Put_Line(‘转账后总余额:’||(account_a+account_b));
EXCEPTION
WHEN OTHERS THEN
Dbms_Output.Put_Line(‘转账失败,业务取消’);
SELECT a.money INTO account_a FROM account_money a WHERE a.Id=‘1001’;
SELECT a.money INTO account_b FROM account_money a WHERE a.Id=‘1002’;
Dbms_Output.Put_Line(‘停止转账后A账户余额:’||account_a);
Dbms_Output.Put_Line(‘停止转账后B账户余额:’||account_b);
Dbms_Output.Put_Line(‘停止转账后总余额:’||(account_a+account_b));
END;
执行上段代码,执行第一遍后,结果如图4-18所示。
图4-18第一次执行账户金额
执行第二遍,结果4-18如图所示。
图4-19第二次执行账户金额
执行第7遍,结果4-20如图所示。
图4-20第七次执行账户金额
结论:当做事务处理后,总额不会发生变化,当出现异常就不会再执行(或者说回滚)。
4.5.2事务的组成和使用
1)数据库事务由以下的部分组成:
一个或多个DML 语句 ;
一个 DDL(Data Definition Language – 数据定义语言) 语句;
一个 DCL(Data Control Language – 数据控制语言)语句;
2)事务的执行开始:
以第一个 DML 语句的执行作为开始 ,以下面的其中之一作为结束:
COMMIT 或 ROLLBACK 语句;
DDL 或 DCL 语句自动提交;
3)事务的执行过程
COMMIT:保存自上一次COMMIT或ROLLBACK以来的所有改变。
ROLLBACK:回滚所有自上一次COMMIT或ROLLBACK以来的所有改变。对dml语句进行的操作,都可以进行回退。
ROLLBACK TO SAVEPOINT:回滚所有的改变到一个已经保存的保存点。
4)事务结束的标志:
用户会话正常结束;
系统异常;
5)事务控制语句的设置
Set transaction:设置事务的属性
Set constrains:设置当前事务的约束模式,设置约束是修改数据的时候,立即起作用,还是当前事务结束后应用。
Savepoint :在事务中建立一个存储点,回滚时,可以指定回滚到什么地方,然后重新执行.
Release savepoint:删除一个存储点
Rollback:回滚事务。取消对数据库所做的任何修改
Commit:提交事务,把事务中对数据库的修改进行永久保存
数据异常:因为Oracle中支持多个事务并发执行,所以会出现下面的数据异常。
错读|脏读:当user1正在读数据库中的表A时,user2正在修改表A,user2修改完了,user1又读一遍表A,user1读出的是修改过的数据,而user2又撤消修改了,user1的读取表A,称为错读或者脏读。
非重复读|不重复读:是指一个事务读取数据库中的数据后,另一个事务则更新了数据,当第一个事务再次读取其中的数据时,就会发现数据已经发生了改变,这就是非重复读取。非重复读取所导致的结果就是一个事务前后两次读取的数据不相同。
假读|幻读:如果一个事务基于某个条件读取数据后,另一个事务则更新了同一个表中的数据,这时第一个事务再次读取数据时,根据搜索的条件返回了不同的行,这就是假读。
事务中遇到的这些异常与事务的隔离性设置有关,事务的隔离性设置越多,异常就出现的越少,但并发效果就越低,事务的隔离性设置越少,异常出现的越多,并发效果越高。