您的位置:首页 > 数据库 > Oracle

|转|oracle 触发器开发

2012-05-29 22:56 281 查看
转发/article/2107329.html

触发器开发的目的:确保数据库满足特定的商业规则和企业逻辑,可以使用触发器,约束,子程序。因为
约束最简单且性能好,所选约束,如果约束不能满足,就用触发器,若触发器都不能满足选择子程序。

一、触发器功能:
1,控制数据安全,在服务器级别控制数据安全是通过授权和回收对象权限来完成的,比如ams用户对scott/tiger 用户对表DML 操作、
grant select ,delete,update,insert on emp to ams;
但是为了实现更复杂的安全模式,比如限制对某个表要修改的数据,就需要用到触发器,比如,只有在9:00到17:00才能改变数据库EMP表对数据库表操作
create or replace trigger triger_stat_before
before insert or update or delete on emp

declare
-- local variables here
begin
if (to_char(sysdate,'HH24') not between '9' and '17') then
RAISE_APPLICATION_ERROR(-20101,'非工作日时间');
end if;
end triger_before;

2,实现数据审计,监视非法和可疑的数据活动,oracle 本身提供了审计功能。如对emp DML 操作以后,
把SQL操作的信息(用户,时间等)写入字典。但是只能审计sql 操作,不能记载数据变化。如果要记载数据
变化必须使用DML触发器。比如工资变化写入数据字典表。
--创建字典表
drop table EMP_CHANGE;
create table EMP_CHANGE(
CNAME VARCHAR2(200),
OLDSAL NUMBER,
NEWSAL NUMBER,
CHANGE_TIME TIMESTAMP(6)
)
--创建触发器
create or replace trigger triger_row_after
after update of sal on emp
for each row
declare
-- local variables here
v_temp number;
begin
select count(*) into v_temp from emp_change where emp_change.cname=:old.ename;
if v_temp =0 then
insert into emp_change values(:old.ename,:old.sal,:new.sal,sysdate);
else
update emp_change set oldSal=:old.sal,newSal=:new.sal,change_time = sysdate
where emp_change.cname = :old.ename;
end if;
end triger_row_after;

执行sql:update emp set sal = sal*1 where deptno=30
会把员工的工资变化情况写入emp_change表

3,实现数据完整性,有写check 做不到的时候就要触发器,比如新工资不能低于原来的工资也不能高于原来工资的20%。
create or replace trigger triger_row_before
before update of sal on emp
for each row
when (new.sal <old.sal or new.sal >old.sal*1.2)
declare
-- local variables here
begin
raise_application_error(-20931,'新工资不能低于原来的工资,并且涨幅不能高于20%');
end triger_row_before;
执行sql: update emp set sal = sal*0.3 where deptno=30
会报错:新工资不能低于原来的工资,并且涨幅不能高于20%

4,实现参照完整性,参照完整性是指两个表有关联关系,主外键关系,当删除主表数据时,从表数据也对应被删除
修改主表的主键列数据时,会级联更新从表相关数据列被修改。级联删除可以通过约束 on delete cascade 关键字
alert table emp add constraint fk_deptno foreign key(deptno) references dept(deptno) on delete cascade;
但是不能实现级联更新。
如:update dept set dept.deptno=50 where dept.deptno=20
违法约束条件,已经找到子记录
触发器可以实现级联更新功能。
create or replace trigger triger_up_casecade
after update of deptno on dept
for each row
declare
-- local variables here
begin
update emp set emp.deptno=:new.deptno where emp.deptno = :old.deptno;
end triger_up_casecade;
执行sql:update dept set dept.deptno =50 where dept.deptno =10
级联更新了emp 表里原来部门号为10的员工部门号为50.

二、触发器是被隐含执行的存储过程,允许基于表和试图的DML操作和基于系统事件(启用数据库,关闭数据库,登录)
1)各种DML触发器:
触发器组成:触发器事件,触发器条件和触发器操作三部分组成
a 触发事件是指引起触发器被触发的sql,数据库事件,用户事件,具体如下
启动关闭例程
oracle 错误消息
用户登录和断开会话
表或者视图上的DML 操作
任何方案上的DDL操作
b 触发器条件是指where子句指定一个表达式
c 触发器操作是指执行sql语句或者pl/sql块

重点是DML触发器。
触发时机 before 表示在在执行DML操作之前触发触发器,after 表示在在执行DML操作之后触发触发器
触发类型 触发事件发生以后,需要执行几次触发操作,如果语句触发器(默认),只会执行一次触发代码
如果指定行触发类型,则会在每个被作用行上执行一次触发器代码。
注意:
对于oracle行级触发器(for each row),不能对本表做任何操作,包括读取
原则: 就是为了防止脏读
在before insert触发器中,可以实现对本表的访问;
在after insert触发器中,不能实现对本表的访问;
在before/after update/delete触发器中,都不能实现对本表的访问。

语句触发器主要用在审计DML操作或者确保DML操作安全执行时,可以使用语句触发器,如:对一个员工表更新之前
检查当前日期是不是周六,或者周日,如果是不能更新。

before 语句触发器:确保DML语句在正常情况下执行
after 语句触发器:审计DML操作,或者在DML操作以后进行汇总操作
before 行触发器:确保数据符合逻辑,使用约束对输入条件加以限制,比如员工新工资不能低于原来的工资
after 行触发器:审计数据变化,比如一个员工工资变化了,把变化了的员工信息写入审计表

限制行触发器:就是在行触发器中,并不是对所有行都触发而是加了条件,格式
create or replace trigger triger_test
before update or delete on trigger_tab
for each row --行触发器都的加上这个语句
where (:old.name='chen')
declare
v_name varchar2(200);
begin
end;

RAISE_APPLICATION_ERROR 方法介绍:
里面的错误代码和内容,都是自定义的。说明是自定义,当然就不是系统中已经命名存在的错误类别,是属于一种自定义事务错误类型,才调用此函数。
error_number_in 之容许从 -20000 到 -20999 之间,这样就不会与 ORACLE 的任何错误代码发生冲突。
error_msg_in 的长度不能超过 2K,否则截取 2K。

:old.name和:new.name 介绍
:new.value是name的新值。
:old.value是name修改前的值。
:old只对Update或者Delete有,对Insert来说,其之前没有数据,所以只能用:new
当在trigges 体引用old 和new,必须加前缀:,和绑定变量的时候一样,申明部分不需要。

sql脚本
create table trigger_tab(
userID number not null primary key,
userName varchar2(200),
age number,
address varchar2(200)
)
insert into trigger_tab(userID,userName,age,address) values(1,'chen',20,'茶陵');
insert into trigger_tab(userID,userName,age,address) values(2,'li',15,'悠闲');
insert into trigger_tab(userID,userName,age,address) values(3,'zh',29,'北京');

创建触发器
create or replace trigger triger_test
before update or delete on trigger_tab
for each row --行触发器都的加上这个语句
declare
-- local variables here
begin
if(:new.age<18)then --:old.age 是更新记录前的值, :new.age是记录更新后的值
RAISE_APPLICATION_ERROR(-20001,'年龄小于18岁,不能删除和修改');
end if;
end triger_test;

command window 窗口执行sql
update trigger_tab t set t.address='长沙' where t.username='li';
后台打印
ORA-20000: 年龄小于18岁,不能修改和删除
ORA-06512: 在"SCOTT.TRIGER_TEST", line 5
ORA-04088: 触发器 'SCOTT.TRIGER_TEST' 执行过程中出错

java 中调用
public class TestTriger {
public static void main(String []args){
String sqlStr = "update trigger_tab t set t.address='长沙' where t.username='li'";
Connection conn = Connector.getConn();
PreparedStatement pstmt;
try {
pstmt = conn.prepareStatement(sqlStr);
int rowNum = pstmt.executeUpdate();
} catch (SQLException e) {
if(e.getErrorCode()==20001){
String errorM = e.getMessage();
System.out.println(errorM);
System.out.println(errorM.split("ORA")[1].replace("-"+e.getErrorCode()+":", ""));
}
}
}
}
捕获到的异常都一个异常Code,这个code 就是在RAISE_APPLICATION_ERROR(-20001,'年龄小于18岁,不能删除和修改');
设置的-20001,去掉前面的负号。
异常信息得到
ORA-20000: 年龄小于18岁,不能修改和删除和修改
ORA-06512: 在"SCOTT.TRIGER_TEST", line 5
ORA-04088: 触发器 'SCOTT.TRIGER_TEST' 执行过程中出错
格式化以后就是想到得到的异常信息了,"年龄小于18岁,不能修改和删除和修改"。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: