Skip to content

Commit 968c928

Browse files
committed
Merge pull request apex-enterprise-patterns#38 from MayTheSForceBeWithYou/master
Issue apex-enterprise-patterns#37 - Bulkify UOW methods
2 parents f2589ec + 4cea773 commit 968c928

File tree

3 files changed

+99
-42
lines changed

3 files changed

+99
-42
lines changed

fflib/src/classes/fflib_ISObjectUnitOfWork.cls

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,22 @@ public interface fflib_ISObjectUnitOfWork
1818
* Register a newly created SObject instance to be inserted when commitWork is called
1919
*
2020
* @param record A newly created SObject instance to be inserted during commitWork
21-
**/
21+
**/
2222
void registerNew(SObject record);
23+
/**
24+
* Register a list of newly created SObject instances to be inserted when commitWork is called
25+
*
26+
* @param records A list of newly created SObject instances to be inserted during commitWork
27+
**/
28+
void registerNew(List<SObject> records);
2329
/**
2430
* Register a newly created SObject instance to be inserted when commitWork is called,
2531
* you may also provide a reference to the parent record instance (should also be registered as new separatly)
2632
*
2733
* @param record A newly created SObject instance to be inserted during commitWork
2834
* @param relatedToParentField A SObjectField reference to the child field that associates the child record with its parent
2935
* @param relatedToParentRecord A SObject instance of the parent record (should also be registered as new separatly)
30-
**/
36+
**/
3137
void registerNew(SObject record, Schema.sObjectField relatedToParentField, SObject relatedToParentRecord);
3238
/**
3339
* Register a relationship between two records that have yet to be inserted to the database. This information will be
@@ -36,22 +42,34 @@ public interface fflib_ISObjectUnitOfWork
3642
* @param record An existing or newly created record
3743
* @param relatedToField A SObjectField referene to the lookup field that relates the two records together
3844
* @param relatedTo A SOBject instance (yet to be commited to the database)
39-
*/
45+
*/
4046
void registerRelationship(SObject record, Schema.sObjectField relatedToField, SObject relatedTo);
4147
/**
4248
* Register an existing record to be updated during the commitWork method
4349
*
4450
* @param record An existing record
45-
**/
51+
**/
4652
void registerDirty(SObject record);
53+
/**
54+
* Register a list of existing records to be updated during the commitWork method
55+
*
56+
* @param records A list of existing records
57+
**/
58+
void registerDirty(List<SObject> records);
4759
/**
4860
* Register an existing record to be deleted during the commitWork method
4961
*
5062
* @param record An existing record
51-
**/
63+
**/
5264
void registerDeleted(SObject record);
65+
/**
66+
* Register a list of existing records to be deleted during the commitWork method
67+
*
68+
* @param records A list of existing records
69+
**/
70+
void registerDeleted(List<SObject> records);
5371
/**
5472
* Takes all the work that has been registered with the UnitOfWork and commits it to the database
55-
**/
56-
void commitWork();
73+
**/
74+
void commitWork();
5775
}

fflib/src/classes/fflib_SObjectUnitOfWork.cls

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,19 @@ public virtual class fflib_SObjectUnitOfWork
151151
registerNew(record, null, null);
152152
}
153153

154+
/**
155+
* Register a list of newly created SObject instances to be inserted when commitWork is called
156+
*
157+
* @param records A list of newly created SObject instances to be inserted during commitWork
158+
**/
159+
public void registerNew(List<SObject> records)
160+
{
161+
for(SObject record : records)
162+
{
163+
registerNew(record, null, null);
164+
}
165+
}
166+
154167
/**
155168
* Register a newly created SObject instance to be inserted when commitWork is called,
156169
* you may also provide a reference to the parent record instance (should also be registered as new separatly)
@@ -202,6 +215,19 @@ public virtual class fflib_SObjectUnitOfWork
202215
m_dirtyMapByType.get(sObjectType).put(record.Id, record);
203216
}
204217

218+
/**
219+
* Register a list of existing records to be updated during the commitWork method
220+
*
221+
* @param records A list of existing records
222+
**/
223+
public void registerDirty(List<SObject> records)
224+
{
225+
for(SObject record : record)
226+
{
227+
this.registerDirty(record);
228+
}
229+
}
230+
205231
/**
206232
* Register an existing record to be deleted during the commitWork method
207233
*
@@ -217,6 +243,19 @@ public virtual class fflib_SObjectUnitOfWork
217243
m_deletedMapByType.get(sObjectType).put(record.Id, record);
218244
}
219245

246+
/**
247+
* Register a list of existing records to be deleted during the commitWork method
248+
*
249+
* @param records A list of existing records
250+
**/
251+
public void registerDeleted(List<SObject> records)
252+
{
253+
for(SObject record : records)
254+
{
255+
this.registerDeleted(record);
256+
}
257+
}
258+
220259
/**
221260
* Takes all the work that has been registered with the UnitOfWork and commits it to the database
222261
**/

fflib/src/classes/fflib_SObjectUnitOfWorkTest.cls

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -40,40 +40,40 @@ private with sharing class fflib_SObjectUnitOfWorkTest
4040
{
4141
// Insert Opporunities with UnitOfWork
4242
{
43-
fflib_SObjectUnitOfWork uow = new fflib_SObjectUnitOfWork(MY_SOBJECTS);
43+
fflib_SObjectUnitOfWork uow = new fflib_SObjectUnitOfWork(MY_SOBJECTS);
4444
for(Integer o=0; o<10; o++)
4545
{
4646
Opportunity opp = new Opportunity();
4747
opp.Name = 'UoW Test Name ' + o;
4848
opp.StageName = 'Open';
4949
opp.CloseDate = System.today();
50-
uow.registerNew(opp);
50+
uow.registerNew(new List<SObject>{opp});
5151
for(Integer i=0; i<o+1; i++)
52-
{
52+
{
5353
Product2 product = new Product2();
5454
product.Name = opp.Name + ' : Product : ' + i;
55-
uow.registerNew(product);
55+
uow.registerNew(new List<SObject>{product});
5656
PricebookEntry pbe = new PricebookEntry();
5757
pbe.UnitPrice = 10;
5858
pbe.IsActive = true;
5959
pbe.UseStandardPrice = false;
6060
pbe.Pricebook2Id = Test.getStandardPricebookId();
61-
uow.registerNew(pbe, PricebookEntry.Product2Id, product);
61+
uow.registerNew(pbe, PricebookEntry.Product2Id, product);
6262
OpportunityLineItem oppLineItem = new OpportunityLineItem();
6363
oppLineItem.Quantity = 1;
6464
oppLineItem.TotalPrice = 10;
6565
uow.registerRelationship(oppLineItem, OpportunityLineItem.PricebookEntryId, pbe);
6666
uow.registerNew(oppLineItem, OpportunityLineItem.OpportunityId, opp);
6767
}
68-
}
68+
}
6969
uow.commitWork();
7070
}
7171

7272
// Assert Results
7373
assertResults('UoW');
7474
// TODO: Need to re-instate this check with a better approach, as it is not possible when
7575
// product triggers contribute to DML (e.g. in sample app Opportunity trigger)
76-
// System.assertEquals(5 /* Oddly a setSavePoint consumes a DML */, Limits.getDmlStatements());
76+
// System.assertEquals(5 /* Oddly a setSavePoint consumes a DML */, Limits.getDmlStatements());
7777

7878
// Records to update
7979
List<Opportunity> opps = [select Id, Name, (Select Id from OpportunityLineItems) from Opportunity where Name like 'UoW Test Name %' order by Name];
@@ -82,11 +82,11 @@ private with sharing class fflib_SObjectUnitOfWorkTest
8282
{
8383
fflib_SObjectUnitOfWork uow = new fflib_SObjectUnitOfWork(MY_SOBJECTS);
8484
Opportunity opp = opps[0];
85-
opp.Name = opp.Name + ' Changed';
86-
uow.registerDirty(opp);
85+
opp.Name = opp.Name + ' Changed';
86+
uow.registerDirty(new List<SObject>{opp});
8787
Product2 product = new Product2();
8888
product.Name = opp.Name + ' : New Product';
89-
uow.registerNew(product);
89+
uow.registerNew(new List<SObject>{product});
9090
PricebookEntry pbe = new PricebookEntry();
9191
pbe.UnitPrice = 10;
9292
pbe.IsActive = true;
@@ -97,21 +97,21 @@ private with sharing class fflib_SObjectUnitOfWorkTest
9797
newOppLineItem.Quantity = 1;
9898
newOppLineItem.TotalPrice = 10;
9999
uow.registerRelationship(newOppLineItem, OpportunityLineItem.PricebookEntryId, pbe);
100-
uow.registerNew(newOppLineItem, OpportunityLineItem.OpportunityId, opp);
100+
uow.registerNew(newOppLineItem, OpportunityLineItem.OpportunityId, opp);
101101
OpportunityLineItem existingOppLine = opp.OpportunityLineItems[0];
102102
// Test that operations on the same object can be daisy chained, and the same object registered as dirty more than once
103103
// This verifies that using a Map to back the dirty records collection prevents duplicate registration.
104104
existingOppLine.Quantity = 2;
105-
uow.registerDirty(existingOppLine);
105+
uow.registerDirty(new List<SObject>{existingOppLine});
106106
existingOppLine.TotalPrice = 20;
107-
uow.registerDirty(existingOppLine);
107+
uow.registerDirty(new List<SObject>{existingOppLine});
108108
uow.commitWork();
109109
}
110110

111111
// Assert Results
112112
// TODO: Need to re-instate this check with a better approach, as it is not possible when
113-
// product triggers contribute to DML (e.g. in sample app Opportunity trigger)
114-
// System.assertEquals(11, Limits.getDmlStatements());
113+
// product triggers contribute to DML (e.g. in sample app Opportunity trigger)
114+
// System.assertEquals(11, Limits.getDmlStatements());
115115
opps = [select Id, Name, (Select Id, PricebookEntry.Product2.Name, Quantity, TotalPrice from OpportunityLineItems Order By PricebookEntry.Product2.Name) from Opportunity where Name like 'UoW Test Name %' order by Name];
116116
System.assertEquals(10, opps.size());
117117
System.assertEquals('UoW Test Name 0 Changed', opps[0].Name);
@@ -124,27 +124,27 @@ private with sharing class fflib_SObjectUnitOfWorkTest
124124
// Delete some records with the UnitOfWork
125125
{
126126
fflib_SObjectUnitOfWork uow = new fflib_SObjectUnitOfWork(MY_SOBJECTS);
127-
uow.registerDeleted(opps[0].OpportunityLineItems[1].PricebookEntry.Product2); // Delete PricebookEntry Product
128-
uow.registerDeleted(opps[0].OpportunityLineItems[1].PricebookEntry); // Delete PricebookEntry
129-
uow.registerDeleted(opps[0].OpportunityLineItems[1]); // Delete OpportunityLine Item
127+
uow.registerDeleted(new List<SObject>{opps[0].OpportunityLineItems[1].PricebookEntry.Product2}); // Delete PricebookEntry Product
128+
uow.registerDeleted(new List<SObject>{opps[0].OpportunityLineItems[1].PricebookEntry}); // Delete PricebookEntry
129+
uow.registerDeleted(new List<SObject>{opps[0].OpportunityLineItems[1]}); // Delete OpportunityLine Item
130130
// Register the same deletions more than once.
131131
// This verifies that using a Map to back the deleted records collection prevents duplicate registration.
132-
uow.registerDeleted(opps[0].OpportunityLineItems[1].PricebookEntry.Product2); // Delete PricebookEntry Product
133-
uow.registerDeleted(opps[0].OpportunityLineItems[1].PricebookEntry); // Delete PricebookEntry
134-
uow.registerDeleted(opps[0].OpportunityLineItems[1]); // Delete OpportunityLine Item
132+
uow.registerDeleted(new List<SObject>{opps[0].OpportunityLineItems[1].PricebookEntry.Product2}); // Delete PricebookEntry Product
133+
uow.registerDeleted(new List<SObject>{opps[0].OpportunityLineItems[1].PricebookEntry}); // Delete PricebookEntry
134+
uow.registerDeleted(new List<SObject>{opps[0].OpportunityLineItems[1]}); // Delete OpportunityLine Item
135135
uow.commitWork();
136-
}
136+
}
137137

138138
// Assert Results
139139
// TODO: Need to re-instate this check with a better approach, as it is not possible when
140-
// product triggers contribute to DML (e.g. in sample app Opportunity trigger)
141-
// System.assertEquals(15, Limits.getDmlStatements());
140+
// product triggers contribute to DML (e.g. in sample app Opportunity trigger)
141+
// System.assertEquals(15, Limits.getDmlStatements());
142142
opps = [select Id, Name, (Select Id, PricebookEntry.Product2.Name, Quantity from OpportunityLineItems Order By PricebookEntry.Product2.Name) from Opportunity where Name like 'UoW Test Name %' order by Name];
143143
List<Product2> prods = [Select Id from Product2 where Name = 'UoW Test Name 0 Changed : New Product'];
144144
System.assertEquals(10, opps.size());
145145
System.assertEquals('UoW Test Name 0 Changed', opps[0].Name);
146146
System.assertEquals(1, opps[0].OpportunityLineItems.size()); // Should have deleted OpportunityLineItem added above
147-
System.assertEquals(0, prods.size()); // Should have deleted Product added above
147+
System.assertEquals(0, prods.size()); // Should have deleted Product added above
148148
}
149149

150150
private static void assertResults(String prefix)
@@ -153,15 +153,15 @@ private with sharing class fflib_SObjectUnitOfWorkTest
153153
String filter = prefix + ' Test Name %';
154154
List<Opportunity> opps = [select Id, Name, (Select Id from OpportunityLineItems) from Opportunity where Name like :filter order by Name];
155155
System.assertEquals(10, opps.size());
156-
System.assertEquals(1, opps[0].OpportunityLineItems.size());
157-
System.assertEquals(2, opps[1].OpportunityLineItems.size());
158-
System.assertEquals(3, opps[2].OpportunityLineItems.size());
159-
System.assertEquals(4, opps[3].OpportunityLineItems.size());
160-
System.assertEquals(5, opps[4].OpportunityLineItems.size());
161-
System.assertEquals(6, opps[5].OpportunityLineItems.size());
162-
System.assertEquals(7, opps[6].OpportunityLineItems.size());
163-
System.assertEquals(8, opps[7].OpportunityLineItems.size());
164-
System.assertEquals(9, opps[8].OpportunityLineItems.size());
165-
System.assertEquals(10, opps[9].OpportunityLineItems.size());
156+
System.assertEquals(1, opps[0].OpportunityLineItems.size());
157+
System.assertEquals(2, opps[1].OpportunityLineItems.size());
158+
System.assertEquals(3, opps[2].OpportunityLineItems.size());
159+
System.assertEquals(4, opps[3].OpportunityLineItems.size());
160+
System.assertEquals(5, opps[4].OpportunityLineItems.size());
161+
System.assertEquals(6, opps[5].OpportunityLineItems.size());
162+
System.assertEquals(7, opps[6].OpportunityLineItems.size());
163+
System.assertEquals(8, opps[7].OpportunityLineItems.size());
164+
System.assertEquals(9, opps[8].OpportunityLineItems.size());
165+
System.assertEquals(10, opps[9].OpportunityLineItems.size());
166166
}
167167
}

0 commit comments

Comments
 (0)