[SalesForce] How to convert a method to Batch Apex

hoping I can get some help with Batch Apex as I have zero experience with it and the docs aren't all that good.

I have a class that does the following:
1. Constructs a Set of Account Ids
2. Constructs a Set of Contract Ids, where the Contract is related to an Account found in step 1.
3. Constructs a Map of
4. Constructs a List of Attachments, updating ParentID from the Map in step 3. (relocating the attachment from the Contract to the root Account)
5. Updates the Attachments to now sit at the Account level.

This is working fine on small amounts of data – but as soon as I test in a full data sandbox it hits the governor limits.

Does anyone know how I could write this class in Batch Apex? I really have no idea.

Thanks.

global class AttachmentReparentPDFsToAccount {
    webservice static string reparentPDFs () {

    // Set containing the Ids of the Accounts that will be migrated
        Set<Id> accountIdSet = new Set<Id> {};
        for (Account acc:[SELECT Id FROM Account WHERE (Type = 'Subscriber' OR Type = 'Ex-Subscriber' OR Type = 'Division' OR Type = 'EDU SUBSCRIBER')]){
            accountIdSet.add(acc.Id);
            System.debug('No. of valid accounts: ' + accountIdSet.size());
        }

        // Set containing the Ids of the Contracts that will be migrated
        Set<Id> contractIdSet = new Set<Id> {};
        for (Contract con:[SELECT Id FROM Contract WHERE AccountId IN :accountIdSet]){
                contractIdSet.add(con.Id);
                System.debug('No. of valid contracts: ' + contractIdSet.size());
        }

        // Create the map that will contain ContractId => Contract.AccountId
        Map<Id,Id> contractToAccountMap=new Map<Id,Id>();

        // Lists of attachments to be created then deleted
        List<Attachment> attachmentsToDelete = new List<Attachment>(); 
        List<Attachment> attachmentsToCreate = new List<Attachment>(); 

        // Populate the map with the ContractId and AccountId related to the Contract
        for(Contract ctr:[Select Id,AccountId from Contract where Id in:contractIdSet]){
            contractToAccountMap.put(ctr.Id,Ctr.AccountId);
        }

        // Attachment tempAtt = new Attachment();
        // Update the parentId of the attachment, changing from the ContractId to the related AccountId
        for(Attachment att:[SELECT Name, Id, Body, ParentId From Attachment WHERE ((Name LIKE '%.pdf') AND (ParentId IN :contractIdSet))]){
            if(contractToAccountMap.get(att.ParentID)!=null){
                Attachment tempAtt = new Attachment ( name = att.name, body = att.body, parentId = contractToAccountMap.get(att.ParentID));
                // tempAtt.ParentID = contractToAccountMap.get(att.ParentID);
                    attachmentsToCreate.add(tempAtt);
                    attachmentsToDelete.add(att);
            }
        }

        // Update the list of attachments
        insert attachmentsToCreate;
        delete attachmentsToDelete;
        System.debug('No. of attachments to be created; ' + attachmentsToCreate.size());

        // Return the no. of attachments that have been reparented
        String listSize =  String.valueOf(attachmentsToCreate.size());
        listSize += ' attachments have been reparented to their root Accounts.';
        return(listSize);

    }
}

Best Answer

I think you can also simplify your code somewhat by adding a sub-select to the SOQL in the start method. You will also need to implement Database.Stateful to pass state between execute and finish methods should you wish to report back to the user (yourself?).

Read up on Batch Apex here http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_batch_interface.htm

global class BatchAttachmentReparentPDFsToAccount implements Database.Batchable<sObject>, Database.Stateful
{
    private String listSize;

    global Database.QueryLocator start( Database.BatchableContext BC ) 
    {
        return Database.getQueryLocator( [SELECT Id,(Select Id From Contracts)  FROM Account WHERE (Type = 'Subscriber' OR Type = 'Ex-Subscriber' OR Type = 'Division' OR Type = 'EDU SUBSCRIBER')] );
    }

    global void execute( Database.BatchableContext BC, List<sObject> scope )
    {
        Set<Id> accountIdSet = new Set<Id> {};
        Map<Id,Id> contractToAccountMap=new Map<Id,Id>();

        for( Account acc : (List<Account>)scope )
        {
            accountIdSet.add( acc.Id );      

            for( Contract ct : acc.Contracts )
            {
                if( !contractToAccountMap.containsKey( ct.Id ) )
                {
                  contractToAccountMap.put( ct.Id, acc.Id );
                }
            }
        }

        // Lists of attachments to be created then deleted
        List<Attachment> attachmentsToDelete = new List<Attachment>(); 
        List<Attachment> attachmentsToCreate = new List<Attachment>(); 


        // Attachment tempAtt = new Attachment();
        // Update the parentId of the attachment, changing from the ContractId to the related AccountId
        List<Attachment> attachments = [SELECT Name, Id, Body, ParentId From Attachment WHERE ((Name LIKE '%.pdf') AND (ParentId IN :contractToAccountMap.keySet() ))];
        for(Attachment att: attachments ){
            if(contractToAccountMap.get(att.ParentID)!=null){
                Attachment tempAtt = new Attachment ( name = att.name, body = att.body, parentId = contractToAccountMap.get(att.ParentID));
                // tempAtt.ParentID = contractToAccountMap.get(att.ParentID);
                    attachmentsToCreate.add(tempAtt);
                    attachmentsToDelete.add(att);
            }
        }

        // Update the list of attachments
        insert attachmentsToCreate;
        delete attachmentsToDelete;
        System.debug('No. of attachments to be created; ' + attachmentsToCreate.size());

        // Return the no. of attachments that have been reparented
        listSize =  String.valueOf(attachmentsToCreate.size());
        listSize += ' attachments have been reparented to their root Accounts.';

    }

    global void finish( Database.BatchableContext BC )
    {
        // send an email with listSize?
    }

}
Related Topic