salesforce 零基础学习(十七)Trigger用法

看本篇之前可以相应阅读以下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方法便可直接使用。如果内容有错误的地方请指正,如果有不懂得问题欢迎留言。另:欢迎加入salesforce cloud群(18203920),里面的大神很nice,在群里学到很多东西。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏开发技术

Cassandra-java操作——基本操作

  接着上篇博客,我们来谈谈java操作cassandra; 上篇博客的环境:jdk1.7 + python2.7.10 + cassandra2.2.8; 由...

792
来自专栏蘑菇先生的技术笔记

AutoMapper使用手册(一)

2294
来自专栏MasiMaro 的技术博文

Windows平台下的内存泄漏检测

在C/C++中内存泄漏是一个不可避免的问题,很多新手甚至有许多老手也会犯这样的错误,下面说明一下在windows平台下如何检测内存泄漏。 在windows平...

1362
来自专栏Java架构师学习

带你深入了解Java线程中的那些事

引言 说到Thread大家都很熟悉,我们平常写并发代码的时候都会接触到,那么我们来看看下面这段代码是如何初始化以及执行的呢? public class Thre...

2978
来自专栏IT杂记

通过Java程序提交通用Mapreduce任务并获取Job信息

背景 我们的一个业务须要有对MR任务的提交和状态跟踪的功能,须要通过Java代码提交一个通用的MR任务(包括mr的jar、配置文件、依赖的第三方jar包),并且...

8145
来自专栏坚毅的PHP

my php & mysql FAQ

php中文字符串长度及定长截取问题使用str_len("中国") 结果为6,php系统默认一个中文字符长度为3,可改用mb_strlen函数获得长度,mb_su...

3526
来自专栏hotqin888的专栏

bootstrap treeview 增删改的正确姿势

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hotqin888/article/det...

1883
来自专栏https://www.cnblogs.com/L

【Linux篇】--awk的使用

awk是一个强大的文本分析工具。相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大。 简单来说awk就是把文件逐行的读入,(空格...

1192
来自专栏大内老A

WCF技术剖析之二十六:如何导出WCF服务的元数据(Metadata)[实现篇]

元数据的导出就是实现从ServiceEndpoint对象向MetadataSet对象转换的过程,在WCF元数据框架体系中,元数据的导出工作由MetadataEx...

2115
来自专栏SDNLAB

Open vSwitch系列之openflow版本兼容

众所周知Open vSwitch支持的openflow版本从1.0到1.5版本(当前Open vSwitch版本是2.3.2)通过阅读代码,处理openflow...

47613

扫码关注云+社区