EBS Form开发中复写on-lock触发器

来源:互联网 发布:卖二手物品的软件 编辑:程序博客网 时间:2024/05/23 10:45
一般情况下二次开发form中(不论是基于表还是基于视图的数据块),不需要复写ON-LOCK触发器,系统会自动处理的很好,那么,出于对form开发更为深入的探索,我决定还是仔细研究研究这个触发器的功用,触发时间,以有相应的pl/sql处理过程等。以此可以作为研究其它触发器的一个范例,找到一个学习研究的方法。


   form端的ON-LOCK触发器用于获取oracle数据表的行级排他锁,当ON-LOCK 触发器fire时,他会试图去获得这个锁,如果这时没有其它session获得这个锁,或者说其它session己经释放这个锁,那么我们的form会很顺利的获得。否则会抛出  app_exceptions.record_lock_exception异常。一般我们会写一个loop ..end loop;块出来,再定义一个计数器counter计录循环次数,把它作为参数传入到app_exception.record_lock_error(counter);处理过程中。这时,当这个counter为2,4,6,8这样的偶数时,在form中会弹出message,用于用户选择是否继续试图获得lock,或中止procedure的执行。因为在app_exception.record_lock_error()过程的最后,会raise一个form_trigger_failure的异常。


   哪么on-lock触发器在什么时候触发呢,帮助中说:“Fires whenever Form Builder would normally attempt to lock a row, such as when an operator presses a key to modify data in an item.  The trigger fires between the keypress and the display of the modified data. ”


   就在当我们进入到form,查询到记录之后,然后,当我们在某一个item中输入内容与这个内容display到显示屏之间那不知多么短的时间内,it fires!


现在我们来看看比较完整的处理代码,为了简便期间,我对代码进行了简化:


  procedure lock_row as


  counter NUMBER;


  cursor C is


   select


    l_id,


    out_date,


    back_date,


    customer,


       from cdm_loan


   where rowid = :loan.row_id


   for update of l_id nowait;              


 ---for update of <table.columons>, <table.columons> nowait;这里"of"只在多表链接查询时才有用,意为锁定指定  列所对应的表,会根据where条件来获得该表的某一行的锁  ,nowait 表示如果获取不了锁,那么直接返 回错误.


recinfo C%rowtype;   


  begin


   fnd_message.debug('lock_row is begin');


   counter := 0;   --循环记数器(变量)


   loop


    begin


     counter := counter+1;


     open C;


     fetch C into recinfo;


     if (C%NOTFOUND) then   


      close C;


      fnd_message.set_name('FND','FORM_RECORD_DELETED');


      fnd_message.error;


      raise form_trigger_failure;


     end if;


      close C;


     if (


  --  检查固定列,数据库中一般为非空字段,form中对应的item的require属性一般为yes,同时,block的query data source columns中相应列的mandatory属性要勾上


           (recinfo.l_id = :loan.l_id)


           AND (recinfo.status = :loan.s_id)


  --  检查非固定列,数据库中一般指为充许空字段,form中对应的item的require属性一般为no,block的query data source columns中相应列的mandatory属性要去掉勾      


          AND ((recinfo.out_date = :loan.out_date)


                OR ((recinfo.out_date is null)


                    AND (:loan.out_date is null)))


             AND ((recinfo.back_date = :loan.back_date)


                OR ((recinfo.back_date is null)


                    AND (:loan.back_date is null)))           


           AND ((recinfo.customer = :loan.customer)


                OR ((recinfo.customer is null)


                    AND (:loan.customer is null)))       


                      )


        then


     return;---满足条件后退出循环


     else--当form中的值同数据库中的值不匹配时的处理  


        fnd_message.set_name('FND', 'FORM_RECORD_CHANGED');


        fnd_message.error;


        raise form_trigger_failure;


    end if;


     exception   ---当不能获取锁时,的异常代码,该异常由系统自动抛出      


        when app_exceptions.record_lock_exception then


              app_exception.record_lock_error(counter);  


     end;


       end loop; 


  end lock_row;


  这里要明白几点:


1、我们调用此ON-LOCK触发器代码时,相应表中的某行即被锁定,众所周知,oracle数据库执行for update时,即获得锁,如果不commit或rollback或断掉session,这个锁是不会被主动释放的。但是在form中,我们只要保存这条记录时,或关掉form窗口,就会释放锁,不论你是否在ON-UPDATE中真正的执行了update语句,系统都会通过自动给你执行一个commit来实现。


2、loop ..end loop中的begin ..end有什么用?  这个begin..end是无奈之举,因为如果没有begin ..end 的话,我们就不能在procedure中的一个特定的范围内处理异常(exceptions语句),这样的结果会出现一旦loop中某一个记录出了问题,那么整个procedure都会中止,当遇到海量数据的处理时,非常不便。因为没有异常处理代码的异常会被传入上一级。 


3,程序要保证在获取锁的时刻form界面上的值要和数据库中值相匹配,才行。如果有不匹配的情况就会强制让你重新查询,再来修改,在修改保存前,你都拥这个锁。


    当第一次进入form时,查询后,在某一个item上输入内容,触发on-lock,这时,眼睛看着界面上item的内容明明改变,但是却成功通过了on-lock中数据一致性的验证,这是为什么呢,因为界面上的内容还没有被保存到相应item的实际value中,要通过保存记录,才会更新界面内容。要明白这一点,可以通过不在on-update中写更新代码来搞清楚。



PROCEDURE LOCK_DATA IS
  counter NUMBER;
  CURSOR c IS
SELECT v.order_type_relationship_id,
  v.xnysc_station_id,
  v.erp_order_type_id,
  v.store_id,
  v.xny_station_name,
  v.transaction_type_id,
  v.erp_order_type_name,
  v.last_update_date,
  v.last_updated_by,
  v.last_update_login,
  v.created_by,
  v.creation_date
FROM   cux_xnysc_order_type_v v
WHERE  v.order_type_relationship_id =
  :cux_order_type_relationship.order_type_relationship_id
FOR    UPDATE OF v.order_type_relationship_id NOWAIT;
  ---for update of <table.columons>, <table.columons> nowait;这里"of"只在多表链接查询时才有用,意为锁定指定  列所对应的表,会根据where条件来获得该表的某一行的锁  ,nowait 表示如果获取不了锁,那么直接返 回错误.
  recinfo c%ROWTYPE;
BEGIN
  fnd_message.debug('lock_row is begin');
  counter := 0; --循环记数器(变量)
  LOOP
BEGIN
 counter := counter + 1;
 OPEN c;
 FETCH c
INTO recinfo;
 IF (c%NOTFOUND)
 THEN
CLOSE c;
fnd_message.set_name('FND',
'FORM_RECORD_DELETED');
fnd_message.error;
RAISE form_trigger_failure;
 END IF;
 CLOSE c;
 IF (
--  检查固定列,数据库中一般为非空字段,form中对应的item的require属性一般为yes,同时,block的query data source columns中相应列的mandatory属性要勾上
 (recinfo.order_type_relationship_id =
 :cux_order_type_relationship.order_type_relationship_id)
--  检查非固定列,数据库中一般指为充许空字段,form中对应的item的require属性一般为no,block的query data source columns中相应列的mandatory属性要去掉勾      
 AND ((recinfo.xnysc_station_id =
 :cux_order_type_relationship.xnysc_station_id) OR
 ((recinfo.xnysc_station_id IS NULL) AND
 (:cux_order_type_relationship.xnysc_station_id IS NULL))) AND
 ((recinfo.erp_order_type_id =
 :cux_order_type_relationship.erp_order_type_id) OR
 ((recinfo.v IS NULL) AND
 (:cux_order_type_relationship.erp_order_type_id IS NULL))))
 THEN
RETURN; ---满足条件后退出循环
 ELSE
--当form中的值同数据库中的值不匹配时的处理  
fnd_message.set_name('FND',
'FORM_RECORD_CHANGED');
fnd_message.error;
RAISE form_trigger_failure;
 END IF;
EXCEPTION
 ---当不能获取锁时,的异常代码,该异常由系统自动抛出      
 WHEN app_exceptions.record_lock_exception THEN
app_exception.record_lock_error(counter);
END;
  END LOOP;
END LOCK_DATA;



0 0
原创粉丝点击