一 . Apex Trigger 觸發器
Apex 觸發器(Apex Triggers)是一種特殊的 Apex 類。 Apex觸發器類似於當特定事件發生時執行的存儲過程。 它在記錄事件發生之前和之後執行。 它的主要作用是在一條記錄被插入、修改、刪除之前或之後自動執行一系列的操作。每一個 Trigger 類必須對應一種對象。
Trigger 的語法和普通的 Apex 類一樣。
Salesforce 建議開發者在創建 Trigger 之前,考慮一下相同的操作可否通過 Salesforce 的設置界面中的功能完成,比如驗證規則(Validation Rule)、工作流規則(Workflow Rule)等。如果可以,則優先使用它們。
二 . Apex Trigger 的結構 和 觸發事件
語法結構 :
//語法結構
trigger triggerName on ObjectName (trigger_events) { Trigger_code_block }
// 說明
trigger Trigger名字(觸發器名字) on 對象名字 (觸發事件) { //Do something }
Trigger 類必須以關鍵字 “trigger” 開始,然後是此 Trigger 的名字。接下來是 “on” 關鍵字,然後是 Trigger 對應的對象的名字。對象名字後面的括號中寫入觸發 Trigger 的事件。
Trigger觸發 的事件分爲以下幾種 :
- insert //添加
- update //修改
- delete //刪除
- merge //合併
- upsert //添加或修改 ,即 無該條數據 就添加 有這條數據則修改
upsert原理:upsert通過是否存在此ID來判斷此條記錄是否存在,
1.如果不存在此ID則執行insert操作;
2.如果存在並且只存在一個ID,則執行update操作;
3.存在並且存在多個ID,則拋出DMLException
- undelete //取消刪除 , 可以取消刪除已刪除並存在於回收站中的記錄
Trigger觸發 的類型分爲以下幾種 :
- Before trigger 通常用於在他們被保存在數據庫以前更新或者校驗數據;
- After trigger 通常用於保存後訪問系統的字段(Id等).
小喵把它們理解爲前置觸發器和後置觸發器,即 在 觸發事件之前 和 觸發事件之後 幹什麼.(這只是小喵的個人理解,只是爲了方便了解/學習)
三 . 設計思想
Trigger設計思想爲 'One Trigger per Object ' 即 ' 一個對象 一個觸發器 ' , 我們 可以將特定對象的所有可能觸發的觸發器組合成一個觸發器.
注意:trigger代碼塊中不能包含static關鍵字。
這種設計模式的好處 :
❃ 可重用性 : 將邏輯放在一個類中,現在可以在觸發器之外重用它,例如在Visualforce頁面,測試類,批處理Apex等中。無需複製代碼!
❃ 簡便性 : 在主觸發器中,每個“觸發器”都減少爲僅兩行代碼。一個面向對象的代碼庫更加有組織的,可預見的和模塊化的呢!
❃ 控制執行順序 : 在Apex中,“不能保證同一對象上的多個觸發器的執行順序。” 這種模式使您可以全面控制訂單。
❃ 代碼風格 : 無論是否利用了上述任何優勢,都將看起來像使用此模式的Apex主服務器。
四 . 常用變量
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;
注意 : 以上布爾類型的變量 , 用於 標誌 Trigger 或數據的狀態 .
- new:返回sObject的記錄的最新的數據的列表;
- newMap:返回一個ID映射到最新的數據列表的Map集合;
- old:返回sObject的記錄修改以前的數據的列表;
- oldMap:返回一個ID映射到修改以前的數據列表的Map集合;
- size:在觸發器中調用的數據總數,包括new和old。
其中, new , new Map , 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.New 和 Trigger.Old 是兩個預定義變量 , 可以用於每一個Trigger類中 . 前者代表了即將被插入、更新的數據 , 後者代表了更新之前、刪除之前的數據 . 它們可能包含一條數據 , 也可能包含一組數據,取決於觸發Trigger 時的狀態 .
注意 : Trigger.New 不存在於 delete 操作中 , 因爲 刪除之後就沒有數據了 ; 而 Trigger.Old 不存在 insert 操作 , 因爲 插入數據之前是沒有數據的.
五 . Trigger 觸發器的使用
目前小喵使用trigger主要有兩種方式 : 第一種是 直接使用trigger , 在trigger內部塊中寫業務邏輯 ; 第二種是 通過Handler 對 trigger 進行封裝 .
1) 直接在Trigger內部塊中寫代碼 :
trigger AccountBeforeDelete on Account (before delete) {
if (Trigger.isBefore) {
// 在SFDelete c中追加數據
if (Trigger.isDelete) {
List<SFDelete__c> dlist = new List<SFDelete__c>();
for (Account a : Trigger.old) {
dlist.add(new SFDelete__c(
delSfId__c = a.Id,
tableName__c = 'Account',
dataOwnerId__c = a.OwnerId
));
}
ControllerUtil.insSFDelete(dlist);
}
}
}
2) 通過Handler 對 trigger 進行封裝 :
通過Handler方式可以將每個Object創建其自身的Handler,將trigger業務邏輯寫在自身的Handler裏面,並通過Factory實例化,達到更好的可擴展性以及可讀性 .
2.1) 創建 TriggerHandler 父類
public abstract class TriggerHandler {
/*
Trigger中,在運行時封裝了new,newMap,old,oldMap變量
其中,new和old返回類型爲List<sObject>
newMap和oldMap返回類型爲Map<Id,sObject>
*/
protected Map<Id,sObject> oldMap{get;set;}
protected Map<Id,sObject> newMap{get;set;}
protected List<sObject> listNew{get;set;}
protected List<sObject> listOld{get;set;}
/*
封裝trigger應該注意以下幾點:
1.trigger.new只能用在insert和update時,且trigger必須是before;
2.trigger.old只能用在update和delete時;
3.trigger.newMap只能用在before update,after insert和after update時;
4.trigger.oldMap只能用在update和delete時.
*/
public interface MyTrigger {
void beforeInsert(SObject currentObject);
void beforeUpdate(SObject oldSobject, SObject currentObject);
void beforeDelete(SObject currentObject);
void afterInsert(SObject currentObject);
void afterUpdate(SObject oldSobject, SObject currentObject);
void afterDelete(SObject currentObject);
Boolean skipExecution();
}
}
2.2) 創建相關對象的Handler , 繼承TriggerHandler 並實現其MyTrigger接口 , 並實現相關方法 .
public class GoodsHandler extends TriggerHandler implements TriggerHandler.MyTrigger {
public GoodsHandler() {
// TODO Construcion
}
public void beforeInsert(SObject currentObject) {
// TODO beforeInsert
}
public void afterInsert(SObject currentObject) {
// TODO afterInsert
}
public void beforeUpdate(SObject oldSobject, SObject currentObject) {
// TODO beforeUpdate
}
public void beforeDelete(SObject currentObject) {
//TODO beforeDelete
}
public void afterUpdate(SObject oldSobject, SObject currentObject) {
}
public void afterDelete(SObject currentObject) {
}
public Boolean skipExecution() {
return false;
}
}
2.3) 創建TriggerFactory,此方法用於實例化Trigger的Handler並執行相應的before或者after操作,其中MyException爲自定義異常類。
public class MyException extends Exception {}
public class TriggerFactory {
/*
實例化Handler,如果不跳過executeTrigger情況下,自動執行Trigger
*/
public static void instanceHandler(Schema.SObjectType objectToken) {
TriggerHandler.MyTrigger myTriggerHandler = getTriggerByObjectToken(objectToken);
if(myTriggerHandler == null) {
throw new MyException('無此object token的trigger');
}
if(!myTriggerHandler.skipExecution()) {
executeTrigger(myTriggerHandler);
}
}
/*
執行trigger應該注意以下幾點:
1.trigger.new只能用在insert和update時,且trigger必須是before;
2.trigger.old只能用在update和delete時;
3.trigger.newMap只能用在before update,after insert和after update時;
4.trigger.oldMap只能用在update和delete時.
*/
public static void executeTrigger(TriggerHandler.MyTrigger myTriggerHandler) {
//trigger分成isBefore以及isAfter
if(Trigger.isBefore) {
if(Trigger.isInsert) {
for (SObject currentObject : Trigger.new)
{
myTriggerHandler.beforeInsert(currentObject);
}
}else if(Trigger.isUpdate) {
for (SObject oldObject : Trigger.old)
{
myTriggerHandler.beforeUpdate(oldObject, Trigger.newMap.get(oldObject.Id));
}
}else if(Trigger.isDelete) {
for (SObject currentObject : Trigger.old)
{
myTriggerHandler.beforeDelete(currentObject);
}
}
} else {//isAfter
if (Trigger.isInsert) {
for (SObject currentObject : Trigger.new) {
myTriggerHandler.afterInsert(currentObject);
}
} else if (Trigger.isUpdate) {
for (SObject oldObject : Trigger.old) {
myTriggerHandler.afterUpdate(oldObject, Trigger.newMap.get(oldObject.Id));
}
} else if (Trigger.isDelete){
for (SObject currentObject : Trigger.old) {
myTriggerHandler.afterDelete(currentObject);
}
}
}
}
/*
此方法用於返回具體某個object的trigger,如果添加一個object的trigger,在此方法添加相應的匹配處理,
同時此object的Handler必須繼承TriggerHandler以及實現TriggerHandler.MyTrigger
每個Object的Object Token不同,所以使用Token作爲參數更加便捷
*/
public static TriggerHandler.MyTrigger getTriggerByObjectToken(Schema.SObjectType objectToken) {
if(objectToken == Goods__c.sObjectType) {
return new GoodsHandler();
}
// TODO 有其他Object需要使用trigger可以繼承TriggerHandler實現其中MyTrigger然後在此處配置
return null;
}
}
2.4) 相應Object的trigger調用Factory的實例化方法
trigger GoodsTrigger on Goods__c (before delete, before update) {
TriggerFactory.instanceHandler(Goods__c.sObjectType);
}
當Goods__c字段進行delete或者update操作時,save以前,會自動觸發GoodsTrigger,GoodsTrigger會執行TriggerFactory的instanceHandler方法,此方法會調用執行instanceHandler以及executeTrigger函數,從而最終將Goods__c表trigger業務邏輯由GoodsHandler類處理。
提示 : 如果業務相對簡單,可以採用第一種方式,開發效率高;如果業務相對複雜,第二種方式可以在相應的Handler模塊更加明瞭的書寫業務邏輯,方便後期維護以及有更好的可讀性
今天分享就到這裏了,下期見!
bye~bye~!
(^_^)~喵~!!