Skip to main content

Apex-Massenauslöser

Lernziele

Nachdem Sie diese Lektion abgeschlossen haben, sind Sie in der Lage, die folgenden Aufgaben auszuführen:

  • Schreiben von Auslösern, die Sammlungen von sObjects bearbeiten
  • Schreiben von Auslösern, die effiziente SOQL- und DML-Vorgänge durchführen
Hinweis

Hinweis

Lernen Sie auf Deutsch? Beginnen Sie die Aufgabe in einem Trailhead Playground in der Sprache Deutsch und verwenden Sie für die Navigation die in Klammern angegebenen Übersetzungen. Kopieren und fügen Sie nur die Angaben in Englisch ein, da zur Überprüfung der Aufgabe Daten in Englisch benötigt werden. Wenn Sie die Aufgabe in Ihrer deutschen Organisation nicht bestehen, empfehlen wir Ihnen folgende Vorgehensweise: (1) Stellen Sie das Gebietsschema auf USA um, (2) legen Sie Englisch als Sprache fest (Anweisungen dazu finden Sie hier) und (3) klicken Sie erneut auf die Schaltfläche "Check Challenge" (Aufgabe überprüfen).

Weitere Details dazu, wie Sie die übersetzte Trailhead-Umgebung optimal nutzen können, finden Sie unter dem Badge "Trailhead in Ihrer Sprache".

Designmuster für Massenauslöser

Apex-Auslöser sind für die Ausführung in einem Massenvorgang optimiert. Es wird empfohlen, für die Verarbeitung von Datensätzen in Auslösern Massen-Designmuster zu verwenden. Bei Verwendung von Massen-Designmustern zeigen Ihre Auslöser eine bessere Leistung, benötigen weniger Serverressourcen und überschreiten seltener Plattformobergrenzen.

Die Vorteile der Strukturierung von Code für Massenvorgänge ist, dass ein solcher Code eine große Anzahl von Datensätzen effizient verarbeiten und innerhalb der Obergrenzen auf der Lightning Platform ausgeführt werden kann. Diese Obergrenzen wurden etabliert, um sicherzustellen, dass unkontrollierter Code gemeinsam genutzte Ressourcen auf der mandantenfähigen Plattform nicht in Beschlag nimmt.

In den folgenden Abschnitten werden die wichtigsten Wege zur Strukturierung von Apex-Code in Auslösern für Massenvorgänge veranschaulicht: Verarbeiten aller Datensätze im Auslöser und Ausführen von SOQL und DML in Sammlungen von sObjects statt jeweils einzelnen sObjects. Die bewährten Vorgehensweisen für SOQL- und DML-Massenvorgänge gelten für jeden Apex-Code, einschließlich SOQL und DML in Klassen. Die aufgeführten Beispiele basieren auf Auslösern und verwenden die Kontextvariable Trigger.new.

Verarbeiten von Datensatzgruppen

Sehen wir uns zuerst das einfachste Massen-Designkonzept in Auslösern an. Massenauslöser verarbeiten alle sObjects im Auslöserkontext. Die Auslöser verarbeiten in der Regel einen Datensatz, wenn die Aktion, durch die der Auslöser ausgeführt wurde, über die Benutzeroberfläche durchgeführt wurde. Wenn der Ursprung der Aktion jedoch ein DML-Massenvorgang oder die API war, verarbeitet der Auslöser eine Datensatzgruppe anstelle von nur einem Datensatz. Wenn Sie beispielsweise eine große Anzahl von Datensätzen über die API importieren, verarbeiten die Auslöser die gesamte Datensatzgruppe. Daher ist es gute Programmierpraxis, stets anzunehmen, dass der Auslöser eine Sammlung von Datensätzen verarbeitet, sodass er in allen Fällen funktioniert.

Im Fall des folgenden Auslösers (MyTriggerNotBulk) wird angenommen, dass nur ein Datensatz zur Ausführung des Auslösers geführt hat. Dieser Auslöser verarbeitet nicht die gesamte Datensatzgruppe, wenn in derselben Transaktion mehrere Datensätze eingefügt werden. Eine Version für Massenvorgänge ist im nächsten Beispiel dargestellt.

trigger MyTriggerNotBulk on Account(before insert) {
    Account a = Trigger.new[0];
    a.Description = 'New description';
}

Dieses Beispiel (MyTriggerBulk) ist eine geänderte Version von MyTriggerNotBulk. Darin wird eine for-Schleife verwendet, um alle verfügbaren sObjects zu durchlaufen. Diese Schleife funktioniert, wenn Trigger.new ein sObject oder mehrere sObjects enthält.

trigger MyTriggerBulk on Account(before insert) {
    for(Account a : Trigger.new) {
        a.Description = 'New description';
    }
}

Durchführen von SOQL-Massenvorgängen

SOQL-Abfragen können äußerst nützlich sein. Sie können verknüpfte Datensätze abrufen und eine Kombination von mehreren Bedingungen in einer Abfrage prüfen. Die Verwendung von SOQL-Funktionen ermöglicht es Ihnen, weniger Code zu schreiben und weniger Abfragen an die Datenbank zu senden. Weniger Datenbankabfragen bedeuten, dass Sie das Überschreiten zulässiger Obergrenzen für Abfragen vermeiden können. Diese liegen bei 100 SOQL-Abfragen für synchronen Apex bzw. 200 für asynchronen Apex.

Der folgende Auslöser (SoqlTriggerNotBulk) zeigt ein SOQL-Abfragemuster, das vermieden werden sollte. In dem Beispiel wird eine SOQL-Abfrage innerhalb einer for-Schleife ausgeführt, um die verknüpften Opportunities für jeden Account abzurufen. Diese wird für jedes Account-sObject in Trigger.new je einmal ausgeführt. Wenn Ihre Liste der Accounts lang ist, kann eine SOQL-Abfrage innerhalb einer for-Schleife zu viele SOQL-Abfragen ergeben. Im nächsten Beispiel wird die empfohlene Vorgehensweise gezeigt.

trigger SoqlTriggerNotBulk on Account(after update) {
    for(Account a : Trigger.new) {
        // Get child records for each account
        // Inefficient SOQL query as it runs once for each account!
        Opportunity[] opps = [SELECT Id,Name,CloseDate
                             FROM Opportunity WHERE AccountId=:a.Id];
        // Do some other processing
    }
}

Dieses Beispiel (SoqlTriggerBulk) ist eine geänderte Version des vorherigen Beispiels und veranschaulicht eine bewährte Vorgehensweise zum Ausführen von SOQL-Abfragen. Die SOQL-Abfrage erledigt den Hauptteil und wird einmal außerhalb der Hauptschleife aufgerufen.

  • Die SOQL-Abfrage verwendet eine innere Abfrage – (SELECT Id FROM Opportunities) – um verknüpfte Opportunities von Accounts abzurufen.
  • Die SOQL-Abfrage wird durch Verwendung der IN-Klausel und Bindung der Variablen Trigger.new in der WHERE-Klausel mit den Datensätzen im Auslöserkontext verbunden – WHERE Id IN :Trigger.new. Diese WHERE-Bedingung filtert die Accounts nach nur den Datensätzen, durch die dieser Auslöser ausgeführt wurde.

Durch Kombination der beiden Teile in der Abfrage werden die gewünschten Datensätze in einem Aufruf abgerufen, d. h. die Accounts in diesem Auslöser mit den verknüpften Opportunities jedes Accounts.

Nachdem die Datensätze und deren verknüpften Datensätze abgerufen wurden, durchläuft die for-Schleife die relevanten Datensätze mithilfe Sammlungsvariablen, in diesem Fall acctsWithOpps. Diese Sammlungsvariable enthält die Ergebnisse der SOQL-Abfrage. Auf diese Weise durchläuft die for-Schleife nur die Datensätze, die wir verarbeiten möchten. Da die verknüpften Datensätze bereits abgerufen wurden, werden in der Schleife keine weiteren Abfragen zum Abrufen dieser Datensätze benötigt.

trigger SoqlTriggerBulk on Account(after update) {
    // Perform SOQL query once.
    // Get the accounts and their related opportunities.
    List<Account> acctsWithOpps =
        [SELECT Id,(SELECT Id,Name,CloseDate FROM Opportunities)
         FROM Account WHERE Id IN :Trigger.new];
    // Iterate over the returned accounts
    for(Account a : acctsWithOpps) {
        Opportunity[] relatedOpps = a.Opportunities;
        // Do some other processing
    }
}

Wenn Sie die übergeordneten Accountdatensätze nicht benötigen, können Sie alternativ nur die Opportunities abrufen, die mit den Accounts innerhalb dieses Auslöserkontexts verknüpft sind. Diese Liste wird in der WHERE-Klausel durch Abgleich des Felds AccountId der Opportunity mit der ID von Accounts in Trigger.new angegeben: WHERE AccountId IN :Trigger.new. Die zurückgegebenen Opportunities stammen von allen Accounts in diesem Auslöserkontext und nicht von einem spezifischen Account. Dieses nächste Beispiel zeigt die Abfrage, die zum Abrufen aller verknüpften Opportunities verwendet wird.

trigger SoqlTriggerBulk on Account(after update) {
    // Perform SOQL query once.
    // Get the related opportunities for the accounts in this trigger.
    List<Opportunity> relatedOpps = [SELECT Id,Name,CloseDate FROM Opportunity
        WHERE AccountId IN :Trigger.new];
    // Iterate over the related opportunities
    for(Opportunity opp : relatedOpps) {
        // Do some other processing
    }
}

Sie können das vorherige Beispiel verkürzen, indem Sie die SOQL-Abfrage mit der for-Schleife in einer Anweisung kombinieren – und zwar zur SOQL-for-Schleife. Nachstehend ist eine andere Version dieses Massenauslösers unter Verwendung einer SOQL-for-Schleife dargestellt.

trigger SoqlTriggerBulk on Account(after update) {
    // Perform SOQL query once.
    // Get the related opportunities for the accounts in this trigger,
    // and iterate over those records.
    for(Opportunity opp : [SELECT Id,Name,CloseDate FROM Opportunity
        WHERE AccountId IN :Trigger.new]) {
        // Do some other processing
    }
}





Hinweis

Auslöser werden für Batches von je 200 Datensätzen ausgeführt. Wenn also 400 Datensätze zur Ausführung eines Auslösers führen, wird der Auslöser zweimal ausgeführt, je einmal für alle 200 Datensätze. Aus diesem Grund können Sie nicht von der Batchverarbeitung von Datensätzen mit einer SOQL-for-Schleife in Auslösern profitieren, da Auslöser Datensätze ebenfalls in Batches verarbeiten. Die SOQL-for-Schleife wird in diesem Beispiel zweimal aufgerufen, wie es auch bei einer eigenständigen SOQL-Abfrage der Fall wäre. Die SOQL-for-Schleife ist dennoch eine elegantere Lösung als das Durchlaufen einer Sammlungsvariablen.

Durchführen von DML-Massenvorgängen

Beim Durchführen von DML-Aufrufen in einem Auslöser oder in einer Klasse führen Sie nach Möglichkeit DML-Aufrufe bei einer Sammlung von sObjects durch. Das Durchführen von DML bei jedem einzelnen sObject bedeutet, dass Ressourcen ineffizient eingesetzt werden. Die Apex-Laufzeit lässt bis zu 150 DML-Aufrufe in einer Transaktion zu.

Dieser Auslöser (DmlTriggerNotBulk) führt einen Aktualisierungsaufruf in einer for-Schleife durch, die verknüpfte Opportunities durchläuft. Wenn bestimmte Bedingungen erfüllt sind, aktualisiert der Auslöser die Opportunity-Beschreibung. In diesem Beispiel wird die Aktualisierungsanweisung auf ineffiziente Weise je einmal für jede Opportunity aufgerufen. Wenn der Auslöser durch einen Massenvorgang zur Accountaktualisierung ausgeführt wurde, kann eine große Anzahl von Accounts vorliegen. Wenn jeder Account über ein oder zwei Opportunities verfügt, kann die Anzahl der Opportunities leicht über 150 betragen. Die Obergrenze für DML-Anweisungen liegt bei 150 Aufrufen.

trigger DmlTriggerNotBulk on Account(after update) {
    // Get the related opportunities for the accounts in this trigger.
    List<Opportunity> relatedOpps = [SELECT Id,Name,Probability FROM Opportunity
        WHERE AccountId IN :Trigger.new];
    // Iterate over the related opportunities
    for(Opportunity opp : relatedOpps) {
        // Update the description when probability is greater
        // than 50% but less than 100%
        if ((opp.Probability >= 50) && (opp.Probability < 100)) {
            opp.Description = 'New description for opportunity.';
            // Update once for each opportunity -- not efficient!
            update opp;
        }
    }
}

Im nächsten Beispiel (DmlTriggerBulk) wird gezeigt, wie Sie DML im Massenvorgang effizient mit nur einem DML-Aufruf in einer Liste der Opportunities ausführen. In dem Beispiel wird das Opportunity-sObject hinzugefügt, um eine Liste von Opportunities (oppsToUpdate) in der Schleife zu aktualisieren. Nachdem alle Opportunities zur Liste hinzugefügt wurden, führt der Auslöser als Nächstes den DML-Aufruf außerhalb der Schleife bei dieser Liste durch. Dieses Muster verwendet nur einen DML-Aufruf, unabhängig von der Anzahl der aktualisierten sObjects.

trigger DmlTriggerBulk on Account(after update) {
    // Get the related opportunities for the accounts in this trigger.
    List<Opportunity> relatedOpps = [SELECT Id,Name,Probability FROM Opportunity
        WHERE AccountId IN :Trigger.new];
    List<Opportunity> oppsToUpdate = new List<Opportunity>();
    // Iterate over the related opportunities
    for(Opportunity opp : relatedOpps) {
        // Update the description when probability is greater
        // than 50% but less than 100%
        if ((opp.Probability >= 50) && (opp.Probability < 100)) {
            opp.Description = 'New description for opportunity.';
            oppsToUpdate.add(opp);
        }
    }
    // Perform DML on a collection
    update oppsToUpdate;
}

Wenden Sie die Designmuster, die Sie kennengelernt haben, an, indem Sie einen Auslöser schreiben, der auf verknüpfte Opportunities von Accounts zugreift. Ändern Sie dazu das Auslöserbeispiel aus dem vorherigen Abschnitt für den Auslöser "AddRelatedRecord". Der Auslöser AddRelatedRecord arbeitet mit einem Massenvorgang, ist jedoch nicht so effizient, wie er sein könnte, da er alle -sObject-Datensätze des Typs Trigger.new durchläuft. Im nächsten Beispiel werden der Auslösercode und die SOQL-Abfrage so geändert, dass nur die relevanten Datensätze abgerufen und diese anschließend durchlaufen werden. Wenn Sie diesen Auslöser noch nicht erstellt haben, ist das kein Problem. Sie können ihn in diesem Abschnitt erstellen.

Beginnen wir mit den Anforderungen für den Auslöser AddRelatedRecord. Der Auslöser wird ausgeführt, nachdem Accounts eingefügt oder aktualisiert wurden. Der Auslöser fügt für jeden Account, der nicht bereits über eine Opportunity verfügt, eine Standard-Opportunity hinzu. 

Zunächst einmal haben neu eingefügte Accounts keine Standard-Opportunity, also müssen wir unbedingt eine hinzufügen. Doch bei aktualisierten Accounts müssen wir herausfinden, ob sie eine zugehörige Opportunity haben oder nicht. Lassen Sie uns also trennen, wie wir Einfügungen und Aktualisierungen verarbeiten, indem wir eine switch-Anweisung für die Kontextvariable Trigger.operationType angeben. Anschließend können Sie die zu verarbeitenden Accounts mit der Variablen toProcess verfolgen. Beispiel:

List<Account> toProcess = null;
switch on Trigger.operationType {
    when AFTER_INSERT {
        // do stuff
    }
    when AFTER_UPDATE {
        // do stuff
    }
}

Bei allen Einfügungen von Accounts weisen wir die neuen Accounts einfach der Liste toProcess zu:

when AFTER_INSERT {
     toProcess = Trigger.New;
}

Bei Aktualisierungen müssen wir herausfinden, welche bestehenden Accounts in diesem Auslöser nicht bereits eine zugehörige Opportunity haben. Da es sich hier um einen "after"-Auslöser handelt, können die betreffenden Datensätze aus der Datenbank abgefragt werden. Hier ist die SOQL-Anweisung, deren Ergebnisse wir der Liste toProcess zuweisen.

when AFTER_UPDATE {
     toProcess = [SELECT Id,Name FROM Account
                  WHERE Id IN :Trigger.New AND
                  Id NOT IN (SELECT AccountId FROM Opportunity WHERE AccountId in :Trigger.New)];
}

Wir arbeiten nun mit einer for-Schleife, um die Liste toProcess mit Accounts zu durchlaufen und eine zugehörige Standard-Opportunity zu oppList hinzuzufügen. Wenn wir fertig sind, fügen wir die Liste der Opportunities mithilfe der DML-Anweisung insert in einem Massenvorgang hinzu.  Sie erfahren nun, wie Sie den vollständigen Auslöser erstellen oder aktualisieren.

  1. Wenn Sie den Auslöser AddRelatedRecord bereits im vorherigen Abschnitt erstellt haben, ändern Sie den Auslöser, indem Sie seinen Inhalt durch folgenden Auslöser ersetzen. Anderenfalls fügen Sie den folgenden Auslöser mithilfe der Entwicklerkonsole hinzu und geben Sie als Auslösernamen AddRelatedRecord ein.


    trigger AddRelatedRecord on Account(after insert, after update) {
        List<Opportunity> oppList = new List<Opportunity>();
        // Add an opportunity for each account if it doesn't already have one.
        // Iterate over accounts that are in this trigger but that don't have opportunities.
        List<Account> toProcess = null;
        switch on Trigger.operationType {
            when AFTER_INSERT {
            // All inserted Accounts will need the Opportunity, so there is no need to perform the query
                toProcess = Trigger.New;
            }
            when AFTER_UPDATE {
                toProcess = [SELECT Id,Name FROM Account
                             WHERE Id IN :Trigger.New AND
                             Id NOT IN (SELECT AccountId FROM Opportunity WHERE AccountId in :Trigger.New)];
            }
        }
        for (Account a : toProcess) {
            // Add a default opportunity for this account
            oppList.add(new Opportunity(Name=a.Name + ' Opportunity',
                                        StageName='Prospecting',
                                        CloseDate=System.today().addMonths(1),
                                        AccountId=a.Id));
        }
        if (oppList.size() > 0) {
            insert oppList;
        }
    }
  2. Um den Auslöser zu testen, erstellen Sie auf der Salesforce-Benutzeroberfläche einen Account und nennen Sie ihn Lions & Cats.
  3. Suchen Sie in der Themenliste "Opportunities" auf der Seite des Accounts nach der neuen Opportunity Lions & Cats. Der Auslöser hat die Opportunity automatisch hinzugefügt!

Ressourcen

Teilen Sie Ihr Trailhead-Feedback über die Salesforce-Hilfe.

Wir würden uns sehr freuen, von Ihren Erfahrungen mit Trailhead zu hören: Sie können jetzt jederzeit über die Salesforce-Hilfe auf das neue Feedback-Formular zugreifen.

Weitere Infos Weiter zu "Feedback teilen"