DML Zugriffe mittels 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
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?
- 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.
- 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.
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.