Salesforce supports basically two types of clones a clone and a deep clone. We’ll go over how to notice the difference later in this post and of course how to create both types. Of course, there will also be some example code on how to create both types of clones.
Caution about Cloning
In general, I think that duplicating data isn’t really a good practice but there are times where it can make sense. I have previously put some posts together about dealing with duplicate data in Salesforce and really think you need to reconsider cloning if the purpose is to create duplicates. There’s a couple of cases where it might make sense to clone an existing record, for example, in some companies they don’t want to reopen an opportunity after it’s already been closed lost.
Up until recently a contact could only be assigned to one account, but thankfully, that’s no longer the case. Another example, is that you may want to override the Clone button and have it go to a visualforce page that leaves certain fields blank. For example, if your SaaS company does annual renewals you might be able to copy all of the existing data and then have a Sales Rep confirm the data and then create the new renewal opportunity.
I like to use cloning when I’m doing some really complex apex and there’s a need to understand what was changed by the user or by code. For example, maybe only certain operations should be done when a certain product has been added or a certain field has been increased or decreased. In this case, the cloned object won’t be inserted it’s simply used to know what was changed.
How to create sObject Clones
Salesforce sObjects have a method called clone which takes optional parameters.
Anything that’s queried can be included as part of the clone, some of the parameters are related to whether to keep the Id, etc. If you are going to keep the Id keep in mind that you cannot insert this record.
The parameters are as follows:
- Preserve Id: determines whether or not if the Id is going to be copied to the clone. The default is false which means the Id isn’t copied.
- Is Deep Clone: determines whether the sObject that is cloned is a fully copy and isn’t simply a reference to the object. If true, the duplicate has it’s own variables stored in memory. This means that if you change the original the clone won’t change or that if you change the clone the original won’t change. The default if the field isn’t provided is false.
- Preserve Readonly TimeStamps: determines if CreatedById, CreatedDate, LastModifiedById, and LastModifiedDate are copied to the clone. I can’t think of any other read-only timestamp fields, but if there are any, their values are also copied to the duplicate. The default is false which means that all of the fields I mentioned would be set if the clone is inserted.
- Preserve AutoNumber: determines whether any autonumbers should have their values copied to the duplicate record. The default is false which means that all autonumbers would be blank and then set if the clone is inserted.
Setting the first parameter to true can cause a lot of issues if you’re planning on inserting the cloned record. Refer to the below code for an example:
Account acc = new Account(Name = 'Original Account'); insert acc; Account clonedAccount = acc.clone(true); insert clonedAccount;
This code would fail with an error that says “Insert failed first error INVALID_FIELD_FOR_INSERT_UPDATE cannot specify Id in an insert call: [Id]”
Doing a clone by setting the first parameter to false would succeed. For example:
Account acc = new Account(Name = 'Original Account'); insert acc; Account clonedAccount = acc.clone(false); insert clonedAccount;
This would create a second account which would have a different Id, but would have the same name. This example is really trivial, but this pattern can be useful for when you need to insert a lot of records quickly and provide some sort of minor difference.
Is Deep Clone
As mentioned, the deep clone creates a complete duplicate in memory including all related objects. This should only be used for comparing records in memory as it doesn’t really work as well as you would think.
Account acc = new Account(Name = 'Original Account'); insert acc; Opportunity opp = new Opportunity(Amount = 100, AccountId = acc.Id); insert opp; // requery the account and opportunity acc = (Account)[select Id, Name, (select Id, Amount from Opportunity) From Account Where Id =: acc.Id]; // make a deep clone of the account & opportunity Account accClone = acc.clone(true, true); // now we could do some sort of comparisons
Note, if you do any sort of DML there’s potential for problems with duplicate records. Deep clones should be used with a lot of caution.
Preserve Readonly Timestamp
I don’t generally use this, as there’s been very few times I thought I would actually need it. It can be done pretty easily by doing the following:
Account acc = new Account(Name = 'Original Account'); insert acc; // make a clone of the account & opportunity Account accClone = acc.clone(true, true, true);
AutoNumbers can be pretty valuable in Salesforce, but they aren’t generally all that useful in isolation. I don’t think I’ve ever specified true to this parameter.
Differences between Clone and Deep Clone
There’s some major differences between the clone and the deep clone.
|Lists if cloned are a duplicate and there’s a reference.||The Deep Clone is duplicated and has no reference to what cloned it|
|A Clone doesn’t keep the Ids||A Deep Clone keeps the Id|
|Doesn’t support primitive data types.||Supports primitive data types.|