Architects / Developers

Dynamic Loading of Parent and Child Object’s Data: A Quick Guide

By Bassem Marji

You can dynamically load multiple records to parent and child objects simultaneously in a single DML call, using an External ID field on the parent object. By delving into this article, you will gain the following:

  • A thorough understanding of what the External ID field is and how it operates
  • A dive into how leveraging External IDs may streamline data integration process

Let’s get started…

Understanding External IDs

An External ID is a standard or custom field in Salesforce that generally holds identifiers coming from external systems such as ERPs or third-party applications.

An External ID supports the following field types:

  • Text
  • Email
  • Number
  • Auto-Number
  • Text Encrypted
READ MORE: A Beginner’s Guide to Salesforce Fields

Leveraging External IDs to Streamline Data Integration

Basically, associating a child record to its parent using an External ID field is an alternative to using the parent record ID field and necessitates the following:

  • Having a parent object with a custom field defined as External ID
  • Creating one or multiple parent records while specifying the External ID relative values
  • Setting a parent record as a nested sObject on the corresponding child records to create

Let’s look at the following practical examples to address a scenario involving the insertion of multiple accounts and contact records in either of two ways: basic and advanced.

The Basic Way

This method involves creating the accounts and then creating the contact records while specifying their parent accounts as nested records.

Refer to this code:

public class CreateParentChild {
    /**
     *Inserting Parent And Child records using multiple DML statements.
    */ 
    public static void basicWay() {
		//Inserting Accounts
    	List<Account> accountList = new List<Account>();
    	
    	for (Integer i=0; i<5; i++) {
        	accountList.add( new Account(Name = 'Test Account_' + i
            	                        ,External_ID__c = 'Test Account External_' + i 
                                    )
                	       );
    	}
    	insert accountList;
    
    	//Inserting Contacts
    	List<Contact> contactList = new List<Contact>();
    	for (Account acc: accountList) {
        	Contact con = new Contact( FirstName = 'First_Name'
            	                      ,LastName  = 'Last_Name'             
                	                  ,Account   = acc
        							 );
        	contactList.add(con); 
    	}
    	insert contactList;
        
    	//USAGE
		System.debug('Number of DML statements used so far: ' + Limits.getDmlStatements());
    }
    
    /**
     *Inserting Parent And Child records dynamically using a single DML statement.
    */ 
    public static void advancedWay() {
           final String parentObjAPIName = 'Account';
 		   final String childObjAPIName  = 'Contact';
              
           //Extracting RelationShip properties between child and parent objects
           String childToParentRelationShipFieldName = null;
           for (Schema.ChildRelationship cr: Schema.getGlobalDescribe()
                	                               .get(parentObjAPIName)
                    	                           .getDescribe()
             									   .getChildRelationships()
               ) 
           {
             if ( String.valueOf(cr.getChildSObject()) == childObjAPIName ) {
                  childToParentRelationShipFieldName =String.valueOf(cr.getField());
             }    
		   }
        
           //Getting the Relationship Name Between Parent and Child
           String childToParentRelationShipName = Schema.getGlobalDescribe()
                	                              .get(childObjAPIName)
                    	                          .getDescribe()
                                                  .fields
                                                  .getMap()
                                                  .get(childToParentRelationShipFieldName)
               									  .getDescribe()
                                                  .getRelationshipName();
           	
       	   List<SObject> parentList = new List<SObject>();
           List<SObject> childList  = new List<SObject>();
           List<SObject> resultList  = new List<SObject>();
        
	       for (Integer i=0; i<5; i++) {
               
                String external_ID = 'Test Account External_' + i;
                    
        		SObject parentObj = Schema.getGlobalDescribe().get(parentObjAPIName).newSObject();
        		parentObj.put('Name'           , ('Test Account_' + i) );
            	parentObj.put('External_ID__c' , external_ID );
            	parentList.add(parentObj);
                
            	//Create a clone of the Parent Object for foreign key reference
            	SObject parObjClone = Schema.getGlobalDescribe().get(parentObjAPIName).newSObject();
            	parObjClone.put('External_ID__c' , external_ID );
            	
            	SObject childObj = Schema.getGlobalDescribe().get(childObjAPIName).newSObject();
            	childObj.put('FirstName'      , ('First_Name_'+i) );
            	childObj.put('LastName'       , ('Last_Name_'+i) );
            	
               	childObj.putSObject(childToParentRelationShipName,parObjClone);
            	childList.add(childObj);
        	}
        
        	resultList.addAll(parentList);
        	resultList.addAll(childList);
        
        	try
            {
        	 insert resultList;
            } catch(Exception e) {
    	       System.debug('Exception=' + e);
        	}
        	
	    	//USAGE
			System.debug('Number of DML statements used so far: ' + Limits.getDmlStatements());
    }
}

Executing this method will consume two DML statements in terms of governor limits.

The Advanced Way

This method involves the following:

  • Specifying the names of the parent and child objects.
  • Extracting the properties of the relationship between child and parent objects.
  • Creating parent accounts.
  • Creating clones of parent accounts for foreign key references. This cloned object serves to overcome a System.DMLException.
  • Creating child records while adding the cloned parent object as a nested sObject throughout the relationship field.

Refer to this code:

public class CreateParentChild {
    /**
     *Inserting Parent And Child records using multiple DML statements.
    */ 
    public static void basicWay() {
		//Inserting Accounts
    	List<Account> accountList = new List<Account>();
    	
    	for (Integer i=0; i<5; i++) {
        	accountList.add( new Account(Name = 'Test Account_' + i
            	                        ,External_ID__c = 'Test Account External_' + i 
                                    )
                	       );
    	}
    	insert accountList;
    
    	//Inserting Contacts
    	List<Contact> contactList = new List<Contact>();
    	for (Account acc: accountList) {
        	Contact con = new Contact( FirstName = 'First_Name'
            	                      ,LastName  = 'Last_Name'             
                	                  ,Account   = acc
        							 );
        	contactList.add(con); 
    	}
    	insert contactList;
        
    	//USAGE
		System.debug('Number of DML statements used so far: ' + Limits.getDmlStatements());
    }
    
    /**
     *Inserting Parent And Child records dynamically using a single DML statement.
    */ 
    public static void advancedWay() {
           final String parentObjAPIName = 'Account';
 		   final String childObjAPIName  = 'Contact';
              
           //Extracting RelationShip properties between child and parent objects
           String childToParentRelationShipFieldName = null;
           for (Schema.ChildRelationship cr: Schema.getGlobalDescribe()
                	                               .get(parentObjAPIName)
                    	                           .getDescribe()
             									   .getChildRelationships()
               ) 
           {
             if ( String.valueOf(cr.getChildSObject()) == childObjAPIName ) {
                  childToParentRelationShipFieldName =String.valueOf(cr.getField());
             }    
		   }
        
           //Getting the Relationship Name Between Parent and Child
           String childToParentRelationShipName = Schema.getGlobalDescribe()
                	                              .get(childObjAPIName)
                    	                          .getDescribe()
                                                  .fields
                                                  .getMap()
                                                  .get(childToParentRelationShipFieldName)
               									  .getDescribe()
                                                  .getRelationshipName();
           	
       	   List<SObject> parentList = new List<SObject>();
           List<SObject> childList  = new List<SObject>();
           List<SObject> resultList  = new List<SObject>();
        
	       for (Integer i=0; i<5; i++) {
               
                String external_ID = 'Test Account External_' + i;
                    
        		SObject parentObj = Schema.getGlobalDescribe().get(parentObjAPIName).newSObject();
        		parentObj.put('Name'           , ('Test Account_' + i) );
            	parentObj.put('External_ID__c' , external_ID );
            	parentList.add(parentObj);
                
            	//Create a clone of the Parent Object for foreign key reference
            	SObject parObjClone = Schema.getGlobalDescribe().get(parentObjAPIName).newSObject();
            	parObjClone.put('External_ID__c' , external_ID );
            	
            	SObject childObj = Schema.getGlobalDescribe().get(childObjAPIName).newSObject();
            	childObj.put('FirstName'      , ('First_Name_'+i) );
            	childObj.put('LastName'       , ('Last_Name_'+i) );
            	
               	childObj.putSObject(childToParentRelationShipName,parObjClone);
            	childList.add(childObj);
        	}
        
        	resultList.addAll(parentList);
        	resultList.addAll(childList);
        
        	try
            {
        	 insert resultList;
            } catch(Exception e) {
    	       System.debug('Exception=' + e);
        	}
        	
	    	//USAGE
			System.debug('Number of DML statements used so far: ' + Limits.getDmlStatements());
    }
}

Executing this method will consume only one DML statement in terms of governor limits.

Note: This method works also when performing updates or upserts.

Hope this advanced method will help you optimize your data ingestion jobs, especially when you have a scarcity in terms of resources.

Summary

This example demonstrates a different approach for handling the creation of multiple parent and child records in a single call, using a custom External ID field defined on the parent object and efficiently managing the available resources.

Any thoughts? Leave them in the comments below!

The Author

Bassem Marji

Bassem is a certified Salesforce Administrator, a Project Implementation Manager at BLOM Bank Lebanon, and a Technical Author at Educative. He is a big fan of the Salesforce ecosystem.

Leave a Reply