看本篇之前可以相應閱讀以下Trigger相關文章:
1.https://developer.salesforce.com/page/Trigger_Frameworks_and_Apex_Trigger_Best_Practices
2.http://chrisaldridge.com/triggers/lightweight-apex-trigger-framework/
3.http://www.sfdc99.com/2015/01/19/the-one-trigger-per-object-design-pattern/
以前以為salesforce中Trigger應用特別簡單,所以沒有列出來單獨講解,和群里大神問問題以后,發現還是很有必要將Trigger單獨寫出來一篇,讓新手更好的了解Trigger。
一.Trigger介紹
Trigger在salesforce記錄更改以前或者以后自動執行,可以執行以下幾種情況:insert,update,delete,merge,upsert,undelete,一個trigger可以同時處理200條records,所以后面所講的new和old變量的返回類型為List類型。
有兩種類型的trigger:
- Before trigger通常用於在他們被保存在數據庫以前更新或者校驗數據;
- After trigger通常用於保存后訪問系統的字段(Id等).
trigger設計的思想為'One Trigger per Object',這種設計的好處詳情查看上方第三個鏈接。所以一個Trigger可以同時設定很多種自動執行的觸發器情況。
eg:trigger GoodsTrigger on Goods__c (before delete, before update) {}:聲明一個trigger針對Goods__c這個Object,當執行delete,update操作以前執行此trigger。
trigger可以有以下的執行類別:before insert,before update,before delete,after insert,after update,after delete,after undelete.
注意:trigger代碼塊中不能包含static關鍵字。
Trigger類中封裝了很多的上下文的變量,這些變量在開發中經常用到。
- isExecuting:當前Apex代碼的上下文環境為trigger環境,而不是VF等則返回true,否則返回false;
- isInsert:當前操作是否為正在執行添加操作,是返回true,否則返回false;
- isUpdate:當前操作是否為正在執行修改操作,是返回true,否則返回false;
- isDelete:當前操作是否為正在執行刪除操作,是返回true,否則返回false;
- isBefore:當前操作是否為在save以前操作,是返回true,否則返回false;
- isAfter:當前操作是否為在save以后操作,是返回true,否則返回false;
- isUndelete:當前操作是否為在回收箱中回復數據以后操作,是返回true,否則返回false;
- new:返回sObject的記錄的最新的數據的列表;
- newMap:返回一個ID映射到最新的數據列表的Map集合;
- old:返回sObject的記錄修改以前的數據的列表;
- oldMap:返回一個ID映射到修改以前的數據列表的Map集合;
- size:在觸發器中調用的數據總數,包括new和old。
這里主要描述一下new,newMap,old以及oldMap,因為他們有使用限制。
- new只適用於執行insert和update的trigger操作時並且類型為before的時候,才可以使用new返回列表;
- newMap只適用於before update,after insert以及after update的trigger操作時,才可以使用newMap返回map集合;
- old以及oldMap只適用於update和delete操作時,才可以使用old以及oldMap。
二.Trigger的使用
目前本人使用trigger主要有兩種方式:第一種為直接使用trigger,在trigger內部塊中寫業務邏輯;第二種為通過Handler對trigger進行封裝。以下是兩種方式的介紹.
1.直接在trigger內部塊中寫代碼。代碼描述如下(業務邏輯不重要,隨便寫的):
1)Goods__c與GoodsSign__c存在LookUp關系;
2)當對Goods__c進行刪除操作時,級聯刪除GoodsSign__c表關聯的數據;
3)當執行update操作時,Goods__c表數據的GoodsName__c字段內容當月發生改變時,GoodsSign__c的GoodsNameSign__c標記成true.
1 trigger GoodsTrigger on Goods__c (before delete, before update) { 2 3 List<Goods__c> goodsListOld = trigger.old; 4 Id goodsSignId; 5 Goods__c goodsOld = goodsListOld.get(0); 6 Datetime lastModifyDatetimeOld; 7 Datetime lastModifyDatetimeNew; 8 GoodsSign__c goodsSign = new GoodsSign__c(); 9 if(trigger.IsDelete) { 10 //delete cascade 11 goodsSignId = goodsOld.Id; 12 List<GoodsSign__c> goodsSignDeleteList = [select Id,Name from GoodsSign__c where Id = :goodsSignId]; 13 try { 14 if(goodsSignDeleteList.size()>0) { 15 delete goodsSignDeleteList; 16 } 17 }catch(Exception e) { 18 //TODO error operation 19 } 20 } else if(trigger.IsUpdate){ 21 List<Goods__c> goodsListNew = trigger.new; 22 Goods__c goodsNew = goodsListNew.get(0); 23 //update operation 24 /* 25 * when first update the record,the system didn't have old.LastModifiedDate 26 * then use CreatedDate instead of LastModifiedDate 27 */ 28 lastModifyDatetimeOld = goodsOld.LastModifiedDate == null ? goodsOld.CreatedDate : goodsOld.LastModifiedDate; 29 lastModifyDatetimeNew = goodsNew.LastModifiedDate; 30 goodsSignId = goodsNew.Id; 31 List<GoodsSign__c> goodsSignAlreadyExistsList = [select CreatedById, CreatedDate, IsDeleted, GoodsSignId__c, GoodsNameSign__c, Name, LastModifiedById, LastModifiedDate, OwnerId, Id, SystemModstamp from GoodsSign__c where GoodsSignId__c = :goodsSignId]; 32 if(goodsSignAlreadyExistsList.size()>0) { 33 goodsSign = goodsSignAlreadyExistsList.get(0); 34 } else { 35 goodsSign.GoodsSignId__c = goodsSignId; 36 } 37 //---------GoodsNameSign----------// 38 if(lastModifyDatetimeNew.year() == lastModifyDatetimeOld.year() && lastModifyDatetimeNew.monthGmt() == lastModifyDatetimeOld.monthGmt()) { 39 String goodsNameNew = goodsNew.GoodsName__c; 40 if(!goodsNameNew.equals(goodsOld.GoodsName__c)) { 41 goodsSign.GoodsNameSign__c = true; 42 } else { 43 goodsSign.GoodsNameSign__c = false; 44 } 45 } else { 46 goodsSign.GoodsNameSign__c = false; 47 } 48 49 try { 50 upsert goodsSign; 51 }catch(Exception e) { 52 53 } 54 55 } 56 57 }
2.通過Handler方式.
通過Handler方式可以將每個Object創建其自身的Handler,將trigger業務邏輯寫在自身的Handler里面,並通過Factory實例化,達到更好的可擴展性以及可讀性,操作步驟如下所示:
1)創建TriggerHandler父類
1 public abstract class TriggerHandler { 2 /* 3 Trigger中,在運行時封裝了new,newMap,old,oldMap變量 4 其中,new和old返回類型為List<sObject> 5 newMap和oldMap返回類型為Map<Id,sObject> 6 */ 7 8 protected Map<Id,sObject> oldMap{get;set;} 9 10 protected Map<Id,sObject> newMap{get;set;} 11 12 protected List<sObject> listNew{get;set;} 13 14 protected List<sObject> listOld{get;set;} 15 16 /* 17 封裝trigger應該注意以下幾點: 18 1.trigger.new只能用在insert和update時,且trigger必須是before; 19 2.trigger.old只能用在update和delete時; 20 3.trigger.newMap只能用在before update,after insert和after update時; 21 4.trigger.oldMap只能用在update和delete時. 22 */ 23 public interface MyTrigger { 24 25 void beforeInsert(SObject currentObject); 26 27 void beforeUpdate(SObject oldSobject, SObject currentObject); 28 29 void beforeDelete(SObject currentObject); 30 31 void afterInsert(SObject currentObject); 32 33 void afterUpdate(SObject oldSobject, SObject currentObject); 34 35 void afterDelete(SObject currentObject); 36 37 Boolean skipExecution(); 38 } 39 }
2)創建相關對象的Handler,繼承TriggerHandler並實現其MyTrigger接口,並實現相關方法。
1 public class GoodsHandler extends TriggerHandler implements TriggerHandler.MyTrigger { 2 3 public GoodsHandler() { 4 // TODO Construcion 5 } 6 7 public void beforeInsert(SObject currentObject) { 8 // TODO beforeInsert 9 } 10 public void afterInsert(SObject currentObject) { 11 // TODO afterInsert 12 } 13 14 public void beforeUpdate(SObject oldSobject, SObject currentObject) { 15 // TODO beforeUpdate 16 } 17 18 public void beforeDelete(SObject currentObject) { 19 //TODO beforeDelete 20 } 21 22 23 24 public void afterUpdate(SObject oldSobject, SObject currentObject) { 25 26 } 27 28 public void afterDelete(SObject currentObject) { 29 30 } 31 32 public Boolean skipExecution() { 33 return false; 34 } 35 }
3)創建TriggerFactory,此方法用於實例化Trigger的Handler並執行相應的before或者after操作,其中MyException為自定義異常類。
1 public class MyException extends Exception { 2 3 }
1 public class TriggerFactory { 2 /* 3 實例化Handler,如果不跳過executeTrigger情況下,自動執行Trigger 4 */ 5 public static void instanceHandler(Schema.SObjectType objectToken) { 6 TriggerHandler.MyTrigger myTriggerHandler = getTriggerByObjectToken(objectToken); 7 if(myTriggerHandler == null) { 8 throw new MyException('無此object token的trigger'); 9 } 10 if(!myTriggerHandler.skipExecution()) { 11 executeTrigger(myTriggerHandler); 12 } 13 } 14 15 /* 16 執行trigger應該注意以下幾點: 17 1.trigger.new只能用在insert和update時,且trigger必須是before; 18 2.trigger.old只能用在update和delete時; 19 3.trigger.newMap只能用在before update,after insert和after update時; 20 4.trigger.oldMap只能用在update和delete時. 21 */ 22 public static void executeTrigger(TriggerHandler.MyTrigger myTriggerHandler) { 23 //trigger分成isBefore以及isAfter 24 if(Trigger.isBefore) { 25 if(Trigger.isInsert) { 26 for (SObject currentObject : Trigger.new) 27 { 28 myTriggerHandler.beforeInsert(currentObject); 29 } 30 }else if(Trigger.isUpdate) { 31 for (SObject oldObject : Trigger.old) 32 { 33 myTriggerHandler.beforeUpdate(oldObject, Trigger.newMap.get(oldObject.Id)); 34 } 35 }else if(Trigger.isDelete) { 36 for (SObject currentObject : Trigger.old) 37 { 38 myTriggerHandler.beforeDelete(currentObject); 39 } 40 } 41 } else {//isAfter 42 if (Trigger.isInsert) { 43 for (SObject currentObject : Trigger.new) { 44 myTriggerHandler.afterInsert(currentObject); 45 } 46 } else if (Trigger.isUpdate) { 47 for (SObject oldObject : Trigger.old) { 48 myTriggerHandler.afterUpdate(oldObject, Trigger.newMap.get(oldObject.Id)); 49 } 50 } else if (Trigger.isDelete){ 51 for (SObject currentObject : Trigger.old) { 52 myTriggerHandler.afterDelete(currentObject); 53 } 54 } 55 } 56 } 57 58 59 /* 60 此方法用於返回具體某個object的trigger,如果添加一個object的trigger,在此方法添加相應的匹配處理, 61 同時此object的Handler必須繼承TriggerHandler以及實現TriggerHandler.MyTrigger 62 每個Object的Object Token不同,所以使用Token作為參數更加便捷 63 */ 64 public static TriggerHandler.MyTrigger getTriggerByObjectToken(Schema.SObjectType objectToken) { 65 if(objectToken == Goods__c.sObjectType) { 66 return new GoodsHandler(); 67 } 68 // TODO 有其他Object需要使用trigger可以繼承TriggerHandler實現其中MyTrigger然后在此處配置 69 return null; 70 } 71 }
4)相應Object的trigger調用Factory的實例化方法
1 trigger GoodsTrigger on Goods__c (before delete, before update) { 2 TriggerFactory.instanceHandler(Goods__c.sObjectType); 3 }
當Goods__c字段進行delete或者update操作時,save以前,會自動觸發GoodsTrigger,GoodsTrigger會執行TriggerFactory的instanceHandler方法,此方法會調用執行instanceHandler以及executeTrigger函數,從而最終將Goods__c表trigger業務邏輯由GoodsHandler類處理。
總結:如果業務相對簡單,可以采用第一種方式,開發效率高;如果業務相對復雜,第二種方式可以在相應的Handler模塊更加明了的書寫業務邏輯,方便后期維護以及有更好的可讀性,有相關需求的童鞋可以copy代碼,並修改其中TriggerFactory的getTriggerByObjectToken方法便可直接使用。如果內容有錯誤的地方請指正,如果有不懂得問題歡迎留言。