|转|oracle 触发器开发

转发http://blog.csdn.net/cdl2008sky/article/details/5129589

触发器开发的目的:确保数据库满足特定的商业规则和企业逻辑,可以使用触发器,约束,子程序。因为
   约束最简单且性能好,所选约束,如果约束不能满足,就用触发器,若触发器都不能满足选择子程序。
   
   一、触发器功能:
   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岁,不能修改和删除和修改"。

原文地址:https://www.cnblogs.com/sumsen/p/2524845.html