Patrick Holder
9. Februar 2018

DML Zugriffe mittels Unit of Work

Unit of Work

In diesem Blogartikel möchte ich ihnen aufzeigen, wie Sie das Unit of Work Prinzip anwenden können und welche Vorteile das Konzept mit sich bringt.

Als Salesforce Berater und Entwickler sehe ich häufig, dass für das Anlegen von mehreren in Relation stehender Salesforce Objekte komplexe und unübersichtliche Code-Konstrukte genutzt werden. Diese Konstrukte sind weder robust, noch wartungsfreundlich.

Andrew Fawcett beschreibt in seinem Buch „Force.com Enterprise Architectur“ wie durch die Utility Class SObjectUnitOfWork Datenbankzugriffe optimiert, transaktionssicher gestaltet und vereinfacht werden können.

Zunächst möchte ich ein Szenario beschreiben, welches in einer Salesforce Organisation Tag für Tag stattfindet. Das Erstellen einer Opportunity und den damit verbundenem Product, PricebookEntry und den OpportunityLineItems.

Der traditionelle Weg ohne Unit of Work

Der herkömmliche Weg das oben beschriebene Szenarien abzubilden, setzt voraus, dass Sie die Objekte in der richtigen Reihenfolge erzeugen, damit alle Abhängigkeiten korrekt erfüllt werden.

Der folgende Code-Ausschnitt bildet den traditionellen Weg mit 10 Opportunities ab.


public class msBulkOppTest {
	
    public static void generateOpps(){
        List opps = new List();
        List<List> productsByOpp = new List<List>();
        List<List> pricebookEntriesByOpp = new List<List>();
        List<List> oppLinesByOpp = new List<List>();

        Pricebook2 pb = [select Id, Name, IsActive from PriceBook2 where IsStandard=True LIMIT 1];

        for(Integer o=0; o<10; o++) {
            Opportunity opp = new Opportunity();
            opp.Name = 'NoUoW Test Name ' + o;
            opp.StageName = 'Open';
            opp.CloseDate = System.today();
            opps.add(opp);
            List products = new List();
            List pricebookEntries = new List();
            List oppLineItems = new List();
            for(Integer i=0; i<o+1; i++) {
                Product2 product = new Product2();
                product.Name = opp.Name + ' : Product : ' + i;
                products.add(product);
                PricebookEntry pbe = new PricebookEntry();
                pbe.UnitPrice = 10;
                pbe.IsActive = true;
                pbe.UseStandardPrice = false;
                pbe.Pricebook2Id = pb.Id;
                pricebookEntries.add(pbe);
                OpportunityLineItem oppLineItem = new OpportunityLineItem();
                oppLineItem.Quantity = 1;
                oppLineItem.TotalPrice = 10;
                oppLineItems.add(oppLineItem);
            }
            productsByOpp.add(products);
            pricebookEntriesByOpp.add(pricebookEntries);
            oppLinesByOpp.add(oppLineItems);
        }
        // Insert Opportunities
        insert opps;
        // Insert Products
        List allProducts = new List();
        for(List products : productsByOpp)
        {
            allProducts.addAll(products);
        }
        insert allProducts;
        // Insert Pricebooks
        Integer oppIdx = 0;
        List allPricebookEntries = new List();
        for(List pricebookEntries : pricebookEntriesByOpp)
        {
            List products = productsByOpp[oppIdx++];
            Integer lineIdx = 0;
            for(PricebookEntry pricebookEntry : pricebookEntries)
            {
                pricebookEntry.Product2Id = products[lineIdx++].Id;
            }
            allPricebookEntries.addAll(pricebookEntries);
        }
        insert allPricebookEntries;
        // Insert Opportunity Lines
        oppIdx = 0;
        List allOppLineItems = new List();
        for(List oppLines : oppLinesByOpp)
        {
            List pricebookEntries = pricebookEntriesByOpp[oppIdx];
            Integer lineIdx = 0;
            for(OpportunityLineItem oppLine : oppLines)
            {
                oppLine.OpportunityId = opps[oppIdx].Id;
                oppLine.PricebookEntryId = pricebookEntries[lineIdx++].Id;
            }
            allOppLineItems.addAll(oppLines);
            oppIdx++;
        }
        insert allOppLineItems;
    }
}

Das Code Beispiel zeigt, dass Sie die Beziehungen zwischen den Records in diversen Listen und Maps abbilden müssen. Denn wenn Sie die einzelnen Records innerhalb einer Schleife einfügen erreichen Sie sehr schnell das Salesforce Limit an zulässigen DML Zugriffen. Jeder der dieses oder ein ähnliches Szenario einmal in Salesforce abgebildet hat, weiß dass es gar nicht so einfach ist einen Überblick darüber zu behalten wann, was und mit welcher Liste eingefügt werden muss.

Mit Unit of Work

Unit of Work

Betrachten wir im Folgenden das gleiche Szenario mit dem Einsatz von Unit of Work. Dabei wird ihnen auffallen wie stark die Komplexität des Codes sinkt und dass der Einsatz von Listen und Maps nicht mehr nötig ist.


public with sharing class msBulkOppTestUoW {

	// SObjects (in order of dependency)
	private static List MY_SOBJECTS = 
		new Schema.SObjectType[] { 
			Product2.SObjectType, 
			PricebookEntry.SObjectType, 
			Opportunity.SObjectType, 
			OpportunityLineItem.SObjectType };

	public msBulkOppTestUoW() {

		Pricebook2 pb = [select Id from Pricebook2 where IsStandard = true];

		SObjectUnitOfWork uow = new SObjectUnitOfWork(MY_SOBJECTS);
		for(Integer o=0; o<10; o++)
		{
		    Opportunity opp = new Opportunity();
		    opp.Name = 'UoW Test Name ' + o;
		    opp.StageName = 'Open';
		    opp.CloseDate = System.today();
		    uow.registerNew(opp);
		    for(Integer i=0; i<o+1; i++)
		    {
		        Product2 product = new Product2();
		        product.Name = opp.Name + ' : Product : ' + i;
		        uow.registerNew(product);
		        PricebookEntry pbe = new PricebookEntry();
		        pbe.UnitPrice = 10;
		        pbe.IsActive = true;
		        pbe.UseStandardPrice = false;
		        pbe.Pricebook2Id = pb.Id;
		        uow.registerNew(pbe, PricebookEntry.Product2Id, product);
		        OpportunityLineItem oppLineItem = new OpportunityLineItem();
		        oppLineItem.Quantity = 1;
		        oppLineItem.TotalPrice = 10;
		        uow.registerRelationship(oppLineItem, OpportunityLineItem.PricebookEntryId, pbe);
		        uow.registerNew(oppLineItem, OpportunityLineItem.OpportunityId, opp);
		    }
		}
		uow.commitWork();		
	}
}

Unit of Work im Detail

Aber wie funktioniert UnitOfWork im Detail?

  1. Zunächst wird die Variable MY_SOBJECTS definiert. Dabei wird festgelegt in welcher Reihenfolge die Objekte eingefügt werden müssen, damit alle Beziehungen korrekt abgebildet werden.
  2. Im nächsten Schritt wird die Utility Class SObjectUnitOfWork instanziiert, dabei wird die zuvor festgelte Insert-Reihenfolge in Form der MY_SOBJECTS Variable übergeben.

Jedes einzufügende Objekt wird durch den Befehl


registerNew(SObject record);

registriert und nicht mehr direkt eingefügt. Falls Sie ein Objekt haben, welches in Beziehung zu weiteren Objekten steht, dann wird diese Beziehung durch den Befehl


registerRelationship(SObject record, Schema.sObjectField relatedToField, SObject relatedTo);

definiert und durch den Befehl


registerNew(SObject record, Schema.sObjectField relatedToParentField, SObject relatedToParentRecord); 

registriert.

Hierbei müssen Sie sich keinerlei Gedanken über Abhängigkeit machen, das Einfügen in der korrekten Reihenfolge übernimmt für Sie Unit of Work.

Abschließend wird der Befehl


uow.commitWork();  

dazu verwendet die registrierten Objekte einzufügen.

Unser E-Book zu den 10 besten Salesforce Add-Ons

E-Book: Die 10 besten Salesforce Add-Ons

Diese 10 Salesforce Add-Ons sollten Sie kennen! Plus: Gute und schlechte Add-Ons erkennen und unterscheiden.

Dieser Blogartikel wurde durch den Artikel „Managing your DML and Transactions with a Unit Of Work“ von Andrew Fawcett inspiriert.

Haben Sie Fragen oder Anregungen? Verfassen Sie einen Kommentar oder melden Sie sich per Mail bei info@mind-force.de. Unser Team wird Ihnen schnellstmöglich antworten.

Patrick Holder

Patrick Holder

Mein Name ist Patrick Holder und ich bin begeisterter Salesforce Consultant und Teamleiter im Fachbereich mindforce. Im Bereich CRM kann ich meine Leidenschaft für IT und wirtschaftliche Prozesse vereinen.

Sie haben Fragen? Kontaktieren Sie mich!



Das könnte Sie auch interessieren

Daten in Salesforce zu importieren bzw. zu exportieren stellt jeden Salesforce Administrator vor die Wahl: Import mit dem Salesforce Import Wizard, dem Apex Dataloader oder Jitterbit. Es bieten sich unterschiedliche Data Management Tools […]

weiterlesen

Wer seine Salesforce-Org jenseits der Standard-Konfigurationsoptionen anpassen möchte, kommt um eigens programmierte Lightning Components kaum herum. Nun lässt sich die UI von Lightning Components mithilfe des Salesforce Lightning Design System […]

weiterlesen

Viele Unternehmen wollen ihre Produkte heutzutage agil umsetzen. Bei der weiteren Planung stellt sich oft ein Unverständnis heraus, was sich hinter diesem Begriff verbirgt. Warum wir bei mindforce agile Vorgehensweise […]

weiterlesen

Schreiben Sie einen Kommentar

Bitte füllen Sie alle mit * gekennzeichneten Felder aus. Ihre E-Mail Adresse wird nicht veröffentlicht.





Kontaktieren Sie uns!
Anja Klusner Kundenservice