function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
SFDCMattSFDCMatt 

Setting external Id for same-object-lookup on Insert

Hi all - I've got a custom object for which there are two basic kinds of records, parents and children. There is a same-object Lookup field on that object that looks up to the object called Object_Self_Lookup__c. I want to use this to relate the children to the parents. 

 

The object also has an external Id called External_Id_Field__c.

 

I'm generating large batches of these records in a scheduled Apex class, and maintaining two different lists, one of parents, one of children. Each record, no matter the type, is assigned a unique value to External_Id_Field__c.

 

My goal is that I can insert the list of Parent records first, having set all their external ids, and then insert the list of children, and have the lookup to the parent record resolved by way of the external Id. I've like to avoid adding a trigger, or making a second pass to get the reference set if it's possible.

 

Is this possible? If so, what am I doing wrong here? This is simplified version of my full code, but this doesn't work either.

 

Thanks!

 

List<Custom_Object__c> parentList = new List<Custom_Object__c>();
List<Custom_Object__c> childList = new List<Custom_Object__c>();

Custom_Object__c objOne = new Custom_Object__c();
objOne.External_Id_Field__c = '1234';
parentList.add(objOne);

Custom_Object__c objTwo = new Custom_Object__c();
objTwo.Object_Self_Lookup__r = objOne;
childList.add(objTwo);

insert parentList;
insert childList;

 

Best Answer chosen by Admin (Salesforce Developers) 
bob_buzzardbob_buzzard

You shouldn't need to insert the parent in order to setup the relationship.  The external id should be good enough for that, as long as the parent is inserted first.  In fact you should be able to create the parent, create the child with a lookup to the parent, put them both into an sobject list (parent first) and insert in one go.  

 

The trick I've found is that if you simply create the parent and populate the name and external id, that fails as the name and external id are considered to be fields that can be match on and you get an error.  Thus to create an account and contact in one go, I had to create the account with name/external id populated, then create the child the with the relationship set to a new version of the account with only the external id field set.  

 

The code is as follows:

 

account acc=new Account(Name='Test', Master_Id__c='Test1234');

contact cont=new Contact(Account=new Account(Master_Id__c='Test1234'), Lastname='Bowden', email='keir.bowden@googlemail.com');

insert new sobject[]{acc, cont};

 

All Answers

SFDCMattSFDCMatt
pmartinpmartin

Matt,

should the insert parentLine;

be just after setting the parent fields

since the parent hasn't been inserted - there isn't the relationship yet.

 

Custom_Object__c objOne = new Custom_Object__c();
objOne.External_Id_Field__c = '1234';
parentList.add(objOne);
insert parentLine;
Custom_Object__c objTwo = new Custom_Object__c();
objTwo.Object_Self_Lookup__r = objOne;
childList.add(objTwo);

insert childList;
SFDCMattSFDCMatt

Hey Peter - In this simplified example, perhaps, but in the real example I'm running through some big loops, doing a bunch of branching logic, creating these records in unique batches based on some conditions.

 

If there are 100 parent records, there will be 300 child records, 3 children per parent. I've got to set the Parent Id during the loops.

 

But I think you're right, given that I'm setting the reference to be objOne, and objOne doesn't exist until the insert, it's not getting set. Hrm.

bob_buzzardbob_buzzard

You shouldn't need to insert the parent in order to setup the relationship.  The external id should be good enough for that, as long as the parent is inserted first.  In fact you should be able to create the parent, create the child with a lookup to the parent, put them both into an sobject list (parent first) and insert in one go.  

 

The trick I've found is that if you simply create the parent and populate the name and external id, that fails as the name and external id are considered to be fields that can be match on and you get an error.  Thus to create an account and contact in one go, I had to create the account with name/external id populated, then create the child the with the relationship set to a new version of the account with only the external id field set.  

 

The code is as follows:

 

account acc=new Account(Name='Test', Master_Id__c='Test1234');

contact cont=new Contact(Account=new Account(Master_Id__c='Test1234'), Lastname='Bowden', email='keir.bowden@googlemail.com');

insert new sobject[]{acc, cont};

 

This was selected as the best answer
BrianWKBrianWK

The sample code I believe is correct (just need to do the insertion of the parent first).


So your logic ends up being something like this?

 

  1. Loop to build a collection of parents - call it lParents
  2. Insert lParents
  3. Loop to build a collection of children - call it lChildren
  4. insert lChildren

The only thing I can think of here is that you insert your parents, then loop through the parents and create your children. So effectively it looks like this:

 

list<Parent__c> lParents = new list<Parent__c>();

for(integer i=0; i < NeededParentCount; i++({
   lParents.add(New Parent__c(External_Id_Field__c = 1234));
}
insert lparents;


list<Children__c> lChildren = new list<Children__c>();
for(integer i=0; i< lParents.size(); i++){
    for(integer x=0; x<3; x++){
      lChidlren.add(New Children__c(Object_Self_lookup__r = lParents[i]));
    }
}
insert lChildren;
   

 Complete guess btw:)

myforcedotcommyforcedotcom

Matt give this a try.

 

List<Custom_Object__c> parentList = new List<Custom_Object__c>();
List<Custom_Object__c> childList = new List<Custom_Object__c>();

Custom_Object__c objOne = new Custom_Object__c();
objOne.External_Id_Field__c = '1234';
parentList.add(objOne);
insert parentList;


Set<String> extIds = new Set<String>();
for(Custom_Object__c objOne : parentList){
	extIds.add(objOne.External_Id_Field__c);
}

//get the list of inserted Parents with ids
List<Custom_Object__c> parents = new List<Custom_Object__c>([Select Id, External_Id_Field__c from Custom_Object__c where External_Id_Field__c in :extIds]);
//put them in a map for recall
Map<String,Custom_Object__c> mapParents = new Map<String,Custom_Object__c>();
for(Custom_Object__c item : parents){
    mapParents.put(item.External_Id_Field__c, item);
}


Custom_Object__c objTwo = new Custom_Object__c();
objTwo.Object_Self_Lookup__r = objOne;
childList.add(objTwo);

for(Custom_Object__c objTwo : childList){
	if(objTwo.Parent_Ext_Id_Field__c != null){
		Custom_Object__c objOne = mapParents.get(objTwo.Parent_Ext_Id_Field__c)
		if(objOne != null){
			objTwo.Parent_LookUp_Field__c = objOne.Id;
		}
	}
}
insert childList;

 

 

 i"ll dm you my address for the six pack!

 

SFDCMattSFDCMatt

Thanks everyone! bob_buzzard's solution of instantiating a new object to set the reference worked perfectly!

SFDCMattSFDCMatt

Just for documentation, here's the code I ended up using (in concept) that worked:

 

List<Custom_Object__c> parentList = new List<Custom_Object__c>();
List<Custom_Object__c> childList = new List<Custom_Object__c>();

Custom_Object__c objOne = new Custom_Object__c();
objOne.External_Id_Field__c = '1234';
parentList.add(objOne);

Custom_Object__c objTwo = new Custom_Object__c();
objTwo.Object_Self_Lookup__r = new Custom_Object__c(External_Id_Field__c = objOne.External_Id_Field__c);
childList.add(objTwo);

insert parentList;
insert childList;