数据库编程之触发器初探即死——ORA-04091表发生了变化 触发器函数不能读它。

概念
触发器是一种特殊类型的存储过程,不由用户直接调用。创建触发器时会对其进行定义,以便在对特定表或列作特定类型的数据修改时执行。
触发器可以查询其他表,而且可以包含复杂的 SQL 语句。 它们主要用于强制服从复杂的业务规则或要求。 例如,表中没有A安全等级的字段,表就不包含A安全等级。
触发器也可用于强制引用完整性,以便在多个表中添加、更新或删除行时,保留在这些表之间所定义的关系。
在这里我本想用它控制字段、数据表、资产目录三表的安全等级的一致性,但是使用过程中我发现触发器无法对触发的表进行操作,哪怕只是查询操作。
使用场景

在数据安全管理中,通过设定数据表各个字段的分类等级,对数据表的结构权限进行控制,最小子颗粒度是数据表的字段,但为了用户的方便应该可以通过资产目录、数据表对字段进行批量操作。在这个过程中我本想用到触发器,目的是通过字段分级表的修改触发资产目录分级表和数据表的分级表,当字段添加等级的时候,如果原先资产目录没有该等级的时候,添加该等级到表和目录(触发器可以实现),如果删除或修改字段等级的时候,判断该表或资产目录下有没有该安全等级的其他字段,如果有,数据分级表和资产分级表不移除该等级,否则的话移除该等级(触发器无法实现)。
实现方式
步骤一:
aq_zdjfj增加一条时,判断aq_zcmlfj和aq_bcjfj的上级相关结点是否包含该安全层级,如果包含该安全等级则不添加,否则的话添加该安全等级到aq_zcmlfj和aq_bcjfj表中,实现方式:
(1)字段等级增加时,触发触发器增加表的相关安全等级

create or replace trigger trig_aq_zdfj
 after   INSERT ON aq_zdjfj
 FOR EACH ROW--对表的每一行触发器执行一次
declare
     bcjnum number:=0;
     --PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
      IF INSERTING THEN
      select count(1) into  bcjnum from aq_bcjfj t where t.bcj_id=:NEW.SJBCJ_ID and t.sjfl_id=:NEW.sjfl_id and t.sjfj_id=:NEW.sjfj_id;
      if bcjnum=0 then 
       insert into aq_bcjfj(FLFJ_ID,BCJ_ID,sjfl_id,sjfj_id,sjzcml_id,lrrq,lrr_dm) values(sys_guid(),:NEW.SJBCJ_ID,:NEW.sjfl_id,:NEW.sjfj_id,:NEW.SJZCML_ID,sysdate,:NEW.lrr_dm);
       END IF;
      end if;
end;
(2)表安全等级增加时,触发触发器增加资产目录的相关安全等级
create or replace trigger trig_aq_bcjfj
 before  INSERT ON aq_bcjfj
 FOR EACH ROW--对表的每一行触发器执行一次
declare
     bcjnum number:=0;
     zcmlnum number:=0;
   cursor cur_syxzcml(arg_zcml_id IN ysj_zcmlb.id%type) is
    select t.*, level
      from ysj_zcmlb t
     start with t.id = arg_zcml_id
    CONNECT BY PRIOR t.parent_id = t.id order by level;
BEGIN
      IF INSERTING THEN
      for syxzcml in cur_syxzcml(:NEW.SJZCML_ID) loop
      select count(1) into  bcjnum from aq_zcmlfj t where t.ZCML_ID=syxzcml.id and t.sjfl_id=:NEW.sjfl_id and t.sjfj_id=:NEW.sjfj_id;
      if bcjnum=0 then
       insert into aq_zcmlfj(FLFJ_ID,zcml_id,sjfl_id,sjfj_id,lrrq,lrr_dm) values(sys_guid(),syxzcml.id,:NEW.sjfl_id,:NEW.sjfj_id,sysdate,:NEW.lrr_dm);
       END IF;
       end loop;
      end if;
end;
步骤二:
aq_zdjfj修改一条安全分级时,判断是否有同级的兄弟节点是同一级别,如果是同一级别的话,则不会影响上级节点aq_zcmlfj和aq_bcjfj的安全等级,否则就需要修改aq_zcmlfj和aq_bcjfj等级,原先思路:aq_zdjfj删除一条时触发触发器,查询aq_zdjfj中的兄弟结点是否有该安全等级,进行操作,但是出现错误了--- ORA-04091:表 发生了变化 触发器 /函数 不能读
遇见问题,分析原因:
触发器不能读取或更新触发语句的任何变异表也包括触发表本身哪怕只是查询。只有一种情况下可以对当前表进行操作就是before insert.
尝试解决:
使用自治事物, PRAGMA AUTONOMOUS_TRANSACTION;COMMIT; ,虽然不报错误“表发生了变化,触发器或函数不能读它”,但是不能根据删除提交,哪怕删除并 COMMIT 之后下次查询条数还是那些,无法减一,最后还是无法使用触发器完成,
最终解决:
只能采用采用的是存储过程实现。
存储过程的具体实现就省略了,mybtias调用存储过程可以在xml直接使用即可
        <select id="sjbflfj" parameterType="java.util.Map" statementType="CALLABLE">
		call aq_bcjflfj(#{zcmlId},#{sbjId},#{sjflId},#{sjfjId},#{lrrDm})
	</select>
拓展一下触发器的相关:
SQL Server 包括两种常规类型的触发器:数据操作语言 (DML) 触发器和数据定义语言 (DDL) 触发器。 当INSERT、UPDATE 或 DELETE 语句修改指定表或视图中的数据时,可以使用 DML 触发器。 DDL 触发器激发存储过程以响应各种 DDL 语句,这些语句主要以CREATE、ALTER 和 DROP 开头。 DDL 触发器可用于管理任务,例如审核和控制数据库操作。
通常说的触发器就是DML触发器。
DML 触发器在 INSERT、UPDATE 和 DELETE 语句上操作,并且有助于在表或视图中修改数据时强制业务规则,扩展数据完整性。
在SQL Server2005后又增加了DDL触发器。
DDL 触发器将激发存储过程以响应事件。但与 DML 触发器不同的是,它们不会为响应针对表或视图的 UPDATE、INSERT 或 DELETE 语句而激发。相反,它们将为了响应各种数据定义语言 (DDL) 事件而激发。这些事件主要与以关键字 CREATE、ALTER 和 DROP 开头的 Transact-SQL 语句对应。执行 DDL 式操作的系统存储过程也可以激发 DDL 触发器。
DDL 触发器使用场合:
  • 要防止对数据库架构进行某些更改。
  • 希望数据库中发生某种情况以响应数据库架构中的更改。
  • 要记录数据库架构中的更改或事件。
 
在这里我们只讲述DML触发器。DML触发器又分以下分类:
1、 After触发器
After触发器要求只有执行某一操作insert、update、delete之后触发器才被触发,且只能定义在表上。
     1)insert触发器
     2)update触发器
     3)delete触发器 
2、Instead of 触发器 
 Instead of 触发器表示并不执行其定义的操作(insert、update、delete)而仅是执行触发器本身。既可以在表上定义instead of触发器,也可以在视图上定义。




猜你喜欢

转载自blog.csdn.net/coder_zyz/article/details/81034852