什么时触发器:
触发器是一个Apex代码,用于在操作记录之前或之后执行操作。
这些操作可以是:
触发器主要有两种类型:
Before Trigger:
用于在将记录的值保存到数据库之前对其进行更新或验证,然后再保存它。
After Trigger:
用于访问系统设置的字段值,并影响记录中的任何更改。换句话说,在这里一般用于更改其他Object的值。
trigger TriggerName on ObjectName (trigger_events) {
code_block
}
trigger_events:可以指定一个或多个,用【,】分割。
例:下边时【Account】Object的触发器写法例
trigger AccountTrigger on Account (before insert, after insert, before update, after update, before delete, after delete, after undelete) {
code_block
}
isInsert | Returns true if the trigger was fired due to an insert operation |
---|---|
isUpdate | Returns true if the trigger was fired due to an update operation |
isDelete | Returns true if the trigger was fired due to a delete operation |
isBefore | Returns true if the trigger has been fired before any record was saved |
isAfter | Returns true if the trigger was fired after all records have been saved |
isUndelete | Returns true if the trigger was fired after a record has been recovered from the Recycle Bin |
new | Returns a list of new versions of the sObject records |
newMap | A map of IDs to the new versions of the sObject records |
old | Returns a list of old versions of the sObject records |
oldMap | A map of IDs to the old versions of the sObject records |
size | The total number of records in a trigger invocation, both old and new |
一般有一下几种场景:
1.check:对画面输入项目进行相关check,也就是业务级别的check,如果是Validation级别的check,不是在Trigger里边实装,而是在【validationRules】里进行。
※validationRules相关做法,会在以后进行讲解。
2.插入更新当前Object,和相关联的Object
3.相关表权限的控制,比如可以实现AccountShare表的增删改。
4.对于各个User数据,进行权限控制,比如可以实现PermissionSetAssignment的增删改。
实战运用例:
例1:利用【Trigger.new】和【Trigger.oldMap】在触发器逻辑中可以判断出某个特定项目是否发生变更。
trigger AccountTrigger on Account (before insert, after insert,
before update, after update,
before delete, after delete, after undelete) {
// 例1:Trigger.new
List<Account> accList = [SELECT Id,Name FROM Account WHERE AccountId IN : Trigger.new];
// 例2:Trigger.isInsert
Id personalAccRTId
= Schema.SObjectType.Account.getRecordTypeInfosByDeveloperName().
get('IndustriesIndividual').getRecordTypeId();
Id corporationAccRTId
= Schema.SObjectType.Account.getRecordTypeInfosByDeveloperName().
get('IndustriesBusiness').getRecordTypeId();
Set<Id> accountRTIdSet = new Set<Id> {personalAccRTId, corporationAccRTId};
Boolean isInsert = Trigger.isInsert ? true : false;
Boolean isUpdate = Trigger.isUpdate ? true : false;
Boolean isDelete = Trigger.isDelete ? true : false;
if (isUpdate) {
for (Account acc : Trigger.new) {
Account oldAccount;
if (Trigger.oldMap.containsKey(acc.Id)) {
oldAccount = Trigger.oldMap.get(acc.Id);
}
if (accountRTIdSet.contains(acc.RecordTypeId)) {
if(oldAccount != null && acc.OwnerId != oldAccount.OwnerId) {
// logic
}
}
}
}
}
例2:利用【Trigger.isBefore】和【Trigger.isAfter】在触发器中特定场景下的处理,一般遵循一下规则:
更新当前Object的项目时用【Before】,更新其它关联Object项目时,用【After】
trigger AccountTrigger on Account (before insert, after insert,
before update, after update,
before delete, after delete, after undelete) {
if (Trigger.isBefore) {
if (Trigger.isDelete) {
// In a before delete trigger, the trigger accesses the records that will be
// deleted with the Trigger.old list.
for (Account a : Trigger.old) {
if (a.name != 'okToDelete') {
a.addError('You can\'t delete this record!');
}
}
} else {
// In before insert or before update triggers, the trigger accesses the new records
// with the Trigger.new list.
for (Account a : Trigger.new) {
if (a.name == 'bad') {
a.name.addError('Bad name');
}
}
}
if (Trigger.isInsert) {
for (Account a : Trigger.new) {
System.assertEquals('xxx', a.accountNumber);
System.assertEquals('industry', a.industry);
System.assertEquals(100, a.numberofemployees);
System.assertEquals(100.0, a.annualrevenue);
a.accountNumber = 'yyy';
}
}
} else {
if (Trigger.isInsert) {
List<Contact> contacts = new List<Contact>();
for (Account a : Trigger.new) {
if(a.Name == 'makeContact') {
contacts.add(new Contact (LastName = a.Name,
AccountId = a.Id));
}
}
insert contacts;
}
}
}
例3:利用触发器可以做check,错误信息也可以放在单独项目上,
trigger AccountTrigger on Account (before insert, after insert,
before update, after update,
before delete, after delete, after undelete) {
Id personalAccRTId
= Schema.SObjectType.Account.getRecordTypeInfosByDeveloperName().
get('IndustriesIndividual').getRecordTypeId();
Id corporationAccRTId
= Schema.SObjectType.Account.getRecordTypeInfosByDeveloperName().
get('IndustriesBusiness').getRecordTypeId();
Set<Id> accountRTIdSet = new Set<Id> {personalAccRTId, corporationAccRTId};
if (Trigger.isBefore) {
if (Trigger.isDelete) {
for (Account currentAccount : Trigger.new) {
if (accountRTIdSet.contains(currentAccount.RecordTypeId)) {
currentAccount.addError(System.Label.MC001);
}
}
}
if (Trigger.isUpdate) {
for (Account acc : Trigger.new) {
Account oldAccount;
if (Trigger.oldMap.containsKey(acc.Id)) {
oldAccount = Trigger.oldMap.get(acc.Id);
}
if (accountRTIdSet.contains(acc.RecordTypeId)) {
if(oldAccount != null && acc.Name != oldAccount.Name) {
acc.Name.addError(System.label.MC002);
}
}
}
}
}
}
所有ErrorMessage都可以定义在下边文件内。
CustomLabels.labels-meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<CustomLabels xmlns="http://soap.sforce.com/2006/04/metadata">
<labels>
<fullName>MC001</fullName>
<language>ja</language>
<protected>false</protected>
<shortDescription>MC089</shortDescription>
<value>このデータは削除できません。</value>
</labels>
<labels>
<fullName>MC002</fullName>
<language>ja</language>
<protected>false</protected>
<shortDescription>MC089</shortDescription>
<value>このデータは更新できません。</value>
</labels>
</CustomLabels>
例4:用户权限相关控制
// 对于当前用户的权限,首先进行删除操作
List<PermissionSetAssignment> permissionSetList
= [SELECT Id,PermissionSetId FROM PermissionSetAssignment
WHERE AssigneeId IN :('用户1的Id','用户2的Id')
AND PermissionSetId IN (SELECT Id
FROM PermissionSet
WHERE IsOwnedByProfile =false
AND Name IN :('一般管理者xx1','一般管理者xx2','一般管理者xx3'))];
Database.delete(permissionSetList);
// 权限表的登录
List<PermissionSetAssignment> pSetAssignmentObjList = new List<PermissionSetAssignment>();
// 权限set表取得权限Setid
Id generalManagerId = [select Id from PermissionSet where Name = :'一般管理者xxxxx'];
List<User> userList = [Select Id FROM User WHERE Name IN :('用户1','用户2')];
for (User user : userList) {
PermissionSetAssignment pSetAssignmentObj = new PermissionSetAssignment();
pSetAssignmentObj.PermissionSetId = generalManagerId;
pSetAssignmentObj.AssigneeId = user.Id;
pSetAssignmentObjList.add(pSetAssignmentObj);
}
Database.insert(pSetAssignmentObjList);
例5:不论是触发器还是ApexClass,利用【System.UserInfo】在代码里边可以直接取得当前登录用户的一般信息。
// 当前用户的UserId
String loginUserId = UserInfo.getUserId();
// TimeZone取得
String tz = UserInfo.getTimeZone().getID();
// 当前用户绑定的ProfileId
String currentProfileId = UserInfo.getProfileId();
例子6:通过Handler对trigger进行封装,ApexClass中分发各种类型的触发器,使各个Object的触发器有更好的扩展性,和可读性,方便后期维护。
public with sharing abstract class TgrHdlExtender {
public String strClassName;
public void exec() {
if(Trigger.new == null && Trigger.old == null) return;
System.debug('[Execute Begin] ' + this.strClassName + ' : ' + Trigger.operationType);
switch on Trigger.operationType {
when BEFORE_INSERT { this.beforeInsert (Trigger.new); }
when BEFORE_UPDATE { this.beforeUpdate (Trigger.old, Trigger.oldMap, Trigger.new, Trigger.newMap); }
when BEFORE_DELETE { this.beforeDelete (Trigger.old, Trigger.oldMap); }
when AFTER_INSERT { this.afterInsert (Trigger.new, Trigger.newMap); }
when AFTER_UPDATE { this.afterUpdate (Trigger.old, Trigger.oldMap, Trigger.new, Trigger.newMap); }
when AFTER_DELETE { this.afterDelete (Trigger.old, Trigger.oldMap); }
when AFTER_UNDELETE { this.afterUndelete (Trigger.new, Trigger.newMap); }
}
System.debug('[Execute End] ' + this.strClassName + ' : ' + Trigger.operationType);
}
protected virtual void beforeInsert (List<SObject> newList) { this.alertMsg(); }
protected virtual void beforeUpdate (List<SObject> oldList, Map<Id, SObject> oldMap, List<SObject> newList, Map<Id, SObject> newMap) { this.alertMsg(); }
protected virtual void beforeDelete (List<SObject> oldList, Map<Id, SObject> oldMap) { this.alertMsg(); }
protected virtual void afterInsert (List<SObject> newList, Map<Id, SObject> newMap) { this.alertMsg(); }
protected virtual void afterUpdate (List<SObject> oldList, Map<Id, SObject> oldMap, List<SObject> newList, Map<Id, SObject> newMap) { this.alertMsg(); }
protected virtual void afterDelete (List<SObject> oldList, Map<Id, SObject> oldMap) { this.alertMsg(); }
protected virtual void afterUndelete (List<SObject> newList, Map<Id, SObject> newMap) { this.alertMsg(); }
private void alertMsg(){
System.debug('[Execute] method is not set.');
}
}
Trigger类
trigger OpportunityTrigger on Opportunity (before insert, after insert, before update, after update, before delete, after delete, after undelete) {
new OpportunityTgrHdl().exec();
}
ApexClass:在Hdl类中写自己的逻辑业务。
public with sharing class OpportunityTgrHdl extends TgrHdlExtender {
public OpportunityTgrHdl() {
if(String.isEmpty(this.strClassName)){
Exception e = new DmlException();
String[] lines = e.getStackTraceString().split('\n');
for (Integer i = lines.size()-1; i >= 0; i--) {
if(lines[i].startsWith('Class.')){
this.strClassName = lines[i].substringBetween('Class.','.');
}
}
}
}
public override void beforeInsert(List<SObject> newList) {
// logic
}
public override void beforeUpdate(List<SObject> oldList, Map<Id, SObject> oldMap, List<SObject> newList, Map<Id, SObject> newMap) {
// logic
}
public override void afterInsert(List<SObject> newList, Map<Id, SObject> newMap) {
// logic
}
public override void afterUpdate(List<SObject> oldList, Map<Id, SObject> oldMap, List<SObject> newList, Map<Id, SObject> newMap) {
// logic
}
public override void afterDelete(List<SObject> oldList, Map<Id, SObject> oldMap) {
// logic
}
}
总结及注意事项:
1. Trigger中不要使用batch去更新数据
2. Before Trigger中只对进入trigger的数据进行字段的更改,不要使用DML操作
3. After Trigger中对除本对象外的数据进行DML操作
4. 写Trigger一定要谨慎小心,避免数据循环进入trigger的情况发生
5. 尽量不要在Trigger逻辑复杂的对象上创建field update的workflow,因为workflow执行顺序在after trigger之后,所以workflow执行update之后,很容易导致trigger被二次调用。
6. Trigger.newMap和Trigger.oldMap都是Read-Only的,不可以在trigger中对其进行更改。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。