Cloning Records in Apex

When you clone an sObject in Apex, it copies all the fields populated in that Apex object, not necessarily all fields on the record.  Let’s say you have a Lead with FirstName, LastName, Company,  LeadSource and Status populated.  If you do the following, the clone will not have LeadSource and Status cloned from the original record.  It will use the default values for Leads. That is because those fields were not queried into the object.

/* query lead and then clone it */
lead l = [select id, firstname, lastname, company from lead where id = '00Q3000000aKwVN' limit 1][0];
lead l2 = l.clone(false, true);
insert l2;

If you are cloning records, it can be frustrating to keep it updated as you add new fields to Salesforce. I generally want all new fields to be cloned too. I’ll code to any exceptions if needed. To help with this, I wrote myself a handy method to build me a SOQL statement and obtain all the writable fields.

public with sharing class Utils{ 

    // Returns a dynamic SOQL statement for the whole object, includes only creatable fields since we will be inserting a cloned result of this query
    public static string getCreatableFieldsSOQL(String objectName, String whereClause){
        
        String selects = '';
        
        if (whereClause == null || whereClause == ''){ return null; }
        
        // Get a map of field name and field token
        Map<String, Schema.SObjectField> fMap = Schema.getGlobalDescribe().get(objectName.toLowerCase()).getDescribe().Fields.getMap();
        list<string> selectFields = new list<string>();
        
        if (fMap != null){
            for (Schema.SObjectField ft : fMap.values()){ // loop through all field tokens (ft)
                Schema.DescribeFieldResult fd = ft.getDescribe(); // describe each field (fd)
                if (fd.isCreateable()){ // field is creatable
                    selectFields.add(fd.getName());
                }
            }
        }
        
        if (!selectFields.isEmpty()){
            for (string s:selectFields){
                selects += s + ',';
            }
            if (selects.endsWith(',')){selects = selects.substring(0,selects.lastIndexOf(','));}
            
        }
        
        return 'SELECT ' + selects + ' FROM ' + objectName + ' WHERE ' + whereClause;
        
    }

}

So if I want to clone the Lead I describe above, I’d do the following and this will ensure that I will clone all the fields on the Lead. Since the method only adds Creatable fields to the SOQL, I don’t have to worry about trying to set a formula field or system field and generating an error.

/* query lead and then clone it */
String soql = Utils.getCreatableFieldsSOQL('lead','id=\'00Q3000000aKwVN\'');
lead l = (Lead)Database.query(soql);
lead l2 = l.clone(false, true);
insert l2;

11 Comments

  1. Benjamin Pirih Said,

    March 31, 2011 @ 3:59 pm

    This code looks familiar 😉

  2. Scott Hemmeter Said,

    March 31, 2011 @ 4:01 pm

    @Ben perhaps, but it’s code I did for a client on a different job than you are thinking. I have used it in a few places.

  3. Benjamin Pirih Said,

    March 31, 2011 @ 4:08 pm

    No worrries it is very useful code. Thanks for sharing it with the community!!

  4. Antonio Said,

    April 29, 2011 @ 1:33 pm

    Very good post. I have very similar need that has come up and the ability to clone fields that may be added in the future is bonus functionality.

    Thanks for the great post!

  5. GON Said,

    June 14, 2011 @ 4:24 am

    Really good post!
    Is there a testing for this class somewhere?
    Thanks in advance!

  6. Sally El Ghoul Said,

    July 26, 2011 @ 1:54 am

    My question is what about if one of the fields is a unique field? it will throw an error how to avoid this case?

  7. Scott Hemmeter Said,

    July 26, 2011 @ 7:31 am

    @Sally, yes you are correct. If that’s the case, then you’d need to add logic between lines 4 and 5 or adjust the main util method.

  8. Hamayoun Khan Said,

    November 3, 2011 @ 4:20 pm

    Great stuff Scott! Thx for this!

  9. Shoby Abdi Said,

    April 5, 2012 @ 1:49 pm

    Force.com Labs app that does the same thing. Plus its unmanaged, so steal the code!

    http://appexchange.salesforce.com/listingDetail?listingId=a0N30000004cbbDEAQ

  10. Margaret Fleischaker Said,

    September 9, 2013 @ 2:33 pm

    This is great, thanks! Question though… does this only work if you are querying for one record? What if you are querying for a list of records and want it to automatically grab all fields?

  11. Margaret Fleischaker Said,

    September 9, 2013 @ 2:42 pm

    Never mind, I just realized that I needed to change the structure of the Database.query method. Works great!

RSS feed for comments on this post