I am trying to do a batch update that enters geocoordinates for 2000 records of a cuctom object called Property__c without gecoordinates using the following code:
Batch
global class batchUpdateAccountGeoLocation implements Database.Batchable<sObject>,Database.AllowsCallouts{
//Start
global Database.QueryLocator start(Database.BatchableContext BC){
string query = 'SELECT Id, Name, Property_Address__c,City__c,State__c,GeoCoord__Latitude__s,GeoCoord__Longitude__s FROM Property__c WHERE Property_Address__c!=null AND GeoCoord__Longitude__s = null';
system.debug('query::'+query);
return Database.getQueryLocator(query);
}
//Execute
global void execute(Database.BatchableContext BC,List<Property__c> scope){
for(Property__c a : scope){
GeoCodeResponse ogc = GeoCodeAddress.getGeoCodeAddress(a.Property_Address__c.replace(' ','+') + ',' + a.City__c + ',' + a.State__c);
if(ogc.status!='ZERO_RESULTS')
{
a.GeoCoord__Latitude__s = ogc.results[0].geometry.location.lat;
a.GeoCoord__Longitude__s = ogc.results[0].geometry.location.lng;
}
}
try{
system.debug('scope::'+scope);
update scope;
}catch(exception e){
system.debug('batchUpdateAccountGeoLocation exception:' + e.getMessage());
}
}
//Finish
global void finish(Database.BatchableContext BC){
}
}
Related Class
public class GeoCodeAddress {
public static GeoCodeResponse getGeoCodeAddress(String Address) {
string url ='https://maps.googleapis.com/maps/api/geocode/json?key=AIzaSyAa-MPRtAoFmdqnSJJIjRvZ7_lcKwwt8oI&address=' + EncodingUtil.urlEncode(Address, 'UTF-8');
// Instantiate a new http object
Http h = new Http();
// Instantiate a new HTTP request, specify the method (GET) as well as the endpoint
HttpRequest req = new HttpRequest();
req.setEndpoint(url);
req.setMethod('GET');
// Send the request, and return a response
HttpResponse res = h.send(req);
system.debug('res::'+res.getBody());
return GeoCodeResponse.parse(res.getBody());
}
}
public class GeoCodeResponse {
public class Address_components {
public String long_name;
public String short_name;
public List<String> types;
}
public List<Results> results;
public String status;
public class Location {
public Double lat;
public Double lng;
}
public class Geometry {
public Location location;
public String location_type;
public Viewport viewport;
}
public class Results {
public List<Address_components> address_components;
public String formatted_address;
public Geometry geometry;
public List<String> types;
}
public class Viewport {
public Location northeast;
public Location southwest;
}
public static GeoCodeResponse parse(String json) {
return (GeoCodeResponse) System.JSON.deserialize(json, GeoCodeResponse.class);
}
}
However, when I run this, I get FATAL_ERROR|System.LimitException: Too many callouts: 101
I know that best practices is to use lists to do these sort of updates, and not execute them in a for loop, but I thought I was already doing that? My for loop does not update anything or make any soql calls. I also do not have any triggers on my Property object.
This is my first time using this sort of batch update so I hope that this is a simple error on my part. I see there are many similar questions, but I was not able to find a straight answer to my problem in any of them. Hopefully I do not get downvoted for this.
EDIT
I changed my execute anonymous statement from
batchUpdateAccountGeoLocation c = new batchUpdateAccountGeoLocation();
Database.executeBatch(c,2000);
to
batchUpdateAccountGeoLocation c = new batchUpdateAccountGeoLocation();
Database.executeBatch(c,100);
Thinking that next I would attempt to update my records in several 100-record chunks and perhaps this would work. To my surprise, it seems to have updated all 2000 of my records. Not sure why this happened, but all my records now have geocoords.
Best Answer
The limit you report in your question is:
meaning that a transaction has attempted to make more than the allowed 100 callouts.
Your execute done like this:
is exactly the right solution because it ensures a maximum of 100 records is passed the the
execute
method (which makes one web service callout per record). The processing may have taken place in 20execute
calls, but thats one of the points of this mechanism: it breaks the work up into defined size blocks that are each processed in their own transaction and so each get their own set of governor limits.PS
The documentation is misleading: you don't need to use
global
in your batchable and you can use compile-time checked static SOQL: