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
Kenji775Kenji775 

A more efficient way to do these updates

hey all. I am working on a batch trigger that will update records called resource bookings. These records are imported via a CSV file with a texts fields that specify an opportunity and and a product. The class needs to look at the list of resource booking records that get passed in, and find any with a null opportunity lookup and populate it based on a field called FFA_OPP_REF__c, and populate a lookup to the product using a field called Sim_Id__c. So if a resource booking record comes in with a null opportunity__c (lookup to opportunity) field, and has an FFA_OPP_REF__C populated, then find the opportunity with the same value in it's Proposal_reference__c field (a text field) and populate the resource booking with the ID of the matching opportunity. Same basic deal with products.

 

The tricky part here is that once that relationship is set, I also need to make use of it later in the same batch run, meaning I need to access values on the related opportunity and product. So it is my understanding that the relationship field change needs to be commited to the database (using database.update or update) before I can actually make use of the relationship. So I have a few questions.

 

1) Do I really need to commit the relationship change to the database before the relationship is 'spiderable' (I can navigate the parent child relationship), or since the lookup has been populate in memory is that sufficient?

 

2) If I do need to update those changes (as outlined in question 1), do I need to requery for the records. Since this is a batchable class, my execute method gets passed a list of resource booking records. If I update their relationships to opportunity and products, and I commit the changes, do I need to query for them again for the relationships to be accessable? 

 

3) Right now my method for updating the relationships is to create 2 maps (defined as <string, list<resource_booking__c>>) . One for resource bookings with null opportunities (keyed by FFA_OPP_REF__C) and one for resource bookings with null products/simulators (keyed by Sim_id__c). I run a query to find the opportunities that match, and one for products that maps. So to commit the changes I end up needing to do a loop that looks like this

 

        for(list<Resource_Booking__c> rbl :nullOpportunityBookings.values())
        {
            database.update(rbl,false);
        }     

 

Is this acceptable, or shoudl I loop over each list to create one big list and push it all at once? If I did create one large list, would I hit governor limit problems, or since I am in a batchable class is that restriction lifted (seems like batch is more lax on the limits).

 

Any input is appreciated. The relevant section of my code is below.

global static void processResourceBookings(list<Resource_Booking__c> resourceBookings)
{

//first for this group of resource booking records, we need to populate the opportunity, and the product on the record.
//those are used for logic later on. So loop over all the resource bookings, find any that have a null in the opportunity or product,
//add them to a map (one map for null opps, and one map for null products/simulators) then run a query to update them.

map<string,list<Resource_Booking__c>> nullSimulatorBookings = new map<string,list<Resource_Booking__c>>();
map<string,list<Resource_Booking__c>> nullOpportunityBookings = new map<string,list<Resource_Booking__c>>();

//loop over all the resource bookings to build the list of them that have a blank opportunity or product/simulator so we know which ones
//we need to populate data for.

for(Resource_Booking__c booking :  resourceBookings)
{
    //build map of resource bookings that do not have a simulator relationship
    if(booking.Simulator__c == null)
    {
        //instantiate a list of bookings.
        list<Resource_Booking__c> bookings;
        
        //if there is no list of bookings for this simulator id, then create one.
        if(!nullSimulatorBookings.containsKey(booking.SIM_ID__c))
        {
            bookings =  new list<Resource_Booking__c>();
        }
        //otherwise fetch the existing list so we can append to it.
        else
        {
            bookings = nullSimulatorBookings.get(booking.SIM_ID__c);
        }
        //add this booking to the list of bookings. 
        bookings.add(booking);
        
        //set this list of bookings to the simulator id.
        nullSimulatorBookings.put(booking.SIM_ID__c,bookings);
    }

    //build map of resource bookings that do not have a opportunity relationship
    if(booking.opportunity__c == null)
    {
        //instantiate a list of bookings.
        list<Resource_Booking__c> bookings;
        
        //if there is no list of bookings for this FFA OPP REF code, then create one.
        if(!nullOpportunityBookings.containsKey(booking.FFA_OPP_REF__c))
        {
            bookings =  new list<Resource_Booking__c>();
        }
        //otherwise fetch the existing list so we can append to it.
        else
        {
            bookings = nullOpportunityBookings.get(booking.FFA_OPP_REF__c);
        }
        //add this booking to the list of bookings. 
        bookings.add(booking);
        
        //set this list of bookings to the  FFA OPP REF code.
        nullOpportunityBookings.put(booking.FFA_OPP_REF__c,bookings);
    }
}


//okay so now we have a list of resource booking records where the product/simulator is null, keyed by the simulator id. So lets run a query and update them.            
if(!nullSimulatorBookings.isEmpty())
{
    //find all the products (simulators) where their simulator id field appears in the list of keys of our null simulator map. This loop returns batches of simulators.
    for(Product2[] simulators :  [select id, Simulator_Id__c from Product2 where Simulator_Id__c in :nullSimulatorBookings.keySet() ])
    {
        //loop over each simulator in this batch.
        for(Product2 simulator : simulators)
        {
            //now get the list of resource bookings that belong to this simulator id and update them all.
            for(Resource_Booking__c thisBooking : nullSimulatorBookings.get(simulator.Simulator_Id__c))
            {
                thisBooking.simulator__c = simulator.Id;
            }
        }
    }                        
}

//commit the changes for every resource booking that got touched in the previous update loop. Yes I know an update loop is ugly, but unless I want to create
//code that creates one big list, then updates that in batches, this is probably the easiest approach. Hopefully it works.
for(list<Resource_Booking__c> rbl :nullOpportunityBookings.values())
{
    database.update(rbl,false);
}        

 //okay so now we have a list of resource booking records where the opportunity is null, keyed by the FFA OPP REF code. So lets run a query and update them.            
if(!nullOpportunityBookings.isEmpty())
{
     //find all the opportunities where their FFA OPP REF (proposal ID) field appears in the list of keys of our null Opportunity map. This loop returns batches of opportunities.
    for(Opportunity[] opportunities :  [select id, Proposal_Reference__c from Opportunity where Proposal_Reference__c in :nullOpportunityBookings.keySet() ])
    {
        //loop over each opportunity in the batch
        for(Opportunity opportunity : opportunities)
        {
            //now get the list of resource bookings that belong to this FFA OPP REF code opportunity and update them all.
            for(Resource_Booking__c thisBooking : nullOpportunityBookings.get(opportunity.Proposal_Reference__c))
            {
                thisBooking.opportunity__c =opportunity.Id;
            }
        }
    }
}      

//commit the changes for every resource booking that got touched in the previous update loop. Yes I know an update loop is ugly, but unless I want to create
//code that creates one big list, then updates that in batches, this is probably the easiest approach. Hopefully it works.           
for(list<Resource_Booking__c> rbl :nullSimulatorBookings.values())
{
    database.update(rbl,false);
}      

/* DO MORE STUFF WITH THE RESOURCE BOOKING RECORDS, INCLUDING UTILIZING THE RELATIONSHIPS THAT WERE JUST POPULATED 
...
...
...
*/