I already have a Schedule apex, that works on 3 objects to do a basic Query and update. I wanted to make this class batch. But unable to add multiple objects in Batch apex and loop around them .
Here is how my Schedule apex looks like
`global class scheduleWorkday implements Schedulable {
global void execute(SchedulableContext ctx) {
//Get Accounts
List<Account> getbdayaccount = [Select ID, Name, Address from Account where State= CT];
if(!getbdayaccount .isEmpty()) {
for(Account a : getbdayaccount ) {
a.name = 'Test';
a.State= 'NJ';
}
update getbdayaccount ;
}
//get Leads
List<Lead> getPreApprovalFollow = [Select ID, Name, State, LeadSource from Lead where State = 'CT' ];
if(!getPreApprovalFollow .isEmpty()) {
for(Lead l: getPreApprovalFollow ) {
l.LeadSource = 'Referral';
l.State = 'NJ';
}
update getPreApprovalFollow ;
}
//get Opportunities
List<Opportunity> getopps = [Select Id, CloseDate, State from Lead where State = 'CT'];
if(!getopps.isEmpty()){
for(Opportunity o : getopps){
o.CloseDate = Date.Today();
o.State = 'CT';
}
update get opps;
}
}
}`
I tried making batch apex something like this -
global class LeadProcessor implements Database.Batchable <SObject> {
//START METHOD
global Database.QueryLocator start(Database.BatchableContext bc){
String Query='Select id,LeadSource, State from Lead where state = 'CT';
return Database.getQueryLocator(Query);
}
//EXECUTE METHOD
global void execute(Database.BatchableContext bc, List<Lead> scope){
for(Lead l: scope){
l.LeadSource='Referral';
l.State = 'NJ';
}
update scope;
}
//FINISH METHOD
global void finish(Database.BatchableContext bc){
}
}
How can I change this batch apex to return multiple queries, add a loop and update them .
That's not how Batch Apex works. You can iterate over exactly one query in a Batch Apex job.
Because you are mutating three different objects in three different ways, the options of batch chaining and use of Dynamic SOQL don't really apply here. You'll simply need to build three different batch classes.
You can run these classes in parallel, or have each one chain into the next in its finish() method. But you can't do it all in one batch.
Related
In my custom object ERT_Case_Type__c,in that i have a check box IsProcessed which is by default false
Now I need to make this IsProcessed Flag set to True whenever a batch Job is executed ,here is Pseudo code, now what i am looking is ,what exact changes do i need to make in below pseudo batch apex code to make this code work with IsProcessed Flag set to True after every batch processed
global class copyertbatch6am implements Database.Batchable {
global Database.QueryLocator start(Database.BatchableContext BC) {
// collect the batches of records or objects to be passed to execute
String query = 'select Case__c, Level_1__c, Level_2__c,Level_3__c FROM ERT_Case_Type__c where createddate = today and IsProcessed Flag = False';
System.debug('ERT Case No is =====>' +query);
return Database.getQueryLocator(query);
}
global void execute(Database.BatchableContext BC, List<ERT_Case_Type__c> exeList) {
// process each batch of records
List<Case_Type__c> listCTD = new List<Case_Type__c>();
System.debug('ERT Case No is =====>' +exeList);
for(ERT_Case_Type__c exe : exeList)
{
listCTD.add(new Case_Type__c(Case__c=exe.Case__c,Level_1__c=exe.Level_1__c,Level_2__c=exe.Level_2__c,Level_3__c=exe.Level_3__c));
IsProcessed Flag = True
}
try {
System.debug('ERT Case No is =====>' +listCTD);
insert listCTD;
} catch(Exception e) {
System.debug(e);
}
}
global void finish(Database.BatchableContext BC) {
// execute any post-processing operations
}
}
Your help is higly appreciated
Thanks and Regards,
Carolyn
I would implement Database.Stateful in this instance. Sorry, SO didn't like the line breaks.
global class ertcopybatch3pm implements Database.Batchable<sObject>, Database.Stateful {
private List<ERT_Case_Type__c> processedRecords;
global Database.QueryLocator start(Database.BatchableContext BC) {
processedRecords = new List<ERT_Case_Type__c>();
// collect the batches of records or objects to be passed to execute
String query = 'select Case__c, Level_1__c, Level_2__c,Level_3__c FROM ERT_Case_Type__c where createddate = today and IsProcessed__c = False';
System.debug('ERT Case No is =====>' +query);
return Database.getQueryLocator(query);
}
global void execute(Database.BatchableContext BC, List<ERT_Case_Type__c> exeList) {
// process each batch of records
List<Case_Type__c> listCTD = new List<Case_Type__c>();
System.debug('ERT Case No is =====>' +exeList);
for(ERT_Case_Type__c exe : exeList)
{
listCTD.add(new Case_Type__c(Case__c=exe.Case__c,Level_1__c=exe.Level_1__c,Level_2__c=exe.Level_2__c,Level_3__c=exe.Level_3__c));
exe.IsProcessed__c = true;
}
try {
System.debug('ERT Case No is =====>' +listCTD);
insert listCTD;
//only successful batches will be processed in the finish() method
processedRecords.addAll(exeList);
} catch(Exception e) {
System.debug(e);
}
}
global void finish(Database.BatchableContext BC) {
// execute any post-processing operations
update processedRecords;
}
}
Implement Database.Stateful in the class definition
Declare a private property to store the processed records upon class instantiation
In the start() method initialize an empty list so you can add items to it
In the execute() method set the flag on the successfully-processed records and add them to the list
In the finish() method update all successfully-processed records from all batches at once
My batch job class is as follows:
global class GroupMemberSearch implements Database.Batchable<SObject>{
global String groupQuery;
global final Map<Id, Group> groupIdMap;
global GroupMemberSearch(String groupQuery){
system.debug(groupQuery);
this.groupQuery = groupQuery;
}
global Database.QueryLocator start(Database.BatchableContext BC){
system.debug('In batch start');
return Database.getQueryLocator(groupQuery);
}
global void execute(Database.BatchableContext BC, List<SObject> scope){
system.debug(scope);
List<groupInfo> grpMemberList = new List<groupInfo>();
for(Group s : (List<Group>)scope){
system.debug(s);
groupInfo newGroup = new groupInfo();
if(s.Name != null){
set<Id> memberIdSet = getGroupMembers(new set<Id>{s.Id}, 0);
if(memberIdSet.size() != 0){
newGroup.groupId = s.Id;
newGroup.groupName = s.Name;
newGroup.groupMemberIds = memberIdSet;
grpMemberList.add(newGroup);
}
}
}
system.debug(grpMemberList);
}
global void finish(Database.BatchableContext BC){}
private set<Id> getGroupMembers(set<Id> groupIds, Integer queryLimit){
set<Id> nestedIds = new set<Id>();
set<Id> returnIds = new set<Id>();
if(queryLimit < 5){
List<GroupMember> members = [SELECT Id, GroupId, UserOrGroupId FROM GroupMember WHERE GroupId IN :groupIds];
for(GroupMember member : members){
if(Schema.Group.SObjectType == member.UserOrGroupId.getSObjectType()){
nestedIds.add(member.UserOrGroupId);
} else{
returnIds.add(member.UserOrGroupId);
}
}
}
queryLimit++;
if(nestedIds.size() > 0){
returnIds.addAll(getGroupMembers(nestedIds, queryLimit));
}
return returnIds;
}
public class groupInfo{
public String groupId;
public String groupName;
public set<Id> groupMemberIds;
}
}
And in my apex controller, I call
Id batchProcessId = Database.executeBatch(new GroupMemberSearch('SELECT Id, OwnerId, Name, Email FROM Group WHERE Name != null'), 20);
which returns a batch Id, and I see the system.debug from the apex batch job constructor... However, I don't see any system.debugs from the start and execute methods... I restricted my log to show only debug statements and they were not in there anywhere. I know my query returns a list of groups, even if it didn't, I should still see the system.debug from the start method before the return...
I want this batch job ran when the page is loaded, thus it is in the constructor of the apex controller.
Any idea why the start and execute methods are not being ran??
Thank you for any help!
I want to add a button to my opportunity header record that is called Insert Products. This will send the opportunity ID to a visualforce page which will have a select file button and an insert button that will loop through the CSV and insert the records to the related opportunity.
This is for non technical users so using Data loader is not an option.
I got this working using standard apex class however hit a limit when i load over 1,000 records (which would happen regularly).
I need to convert this to a batch process however am not sure how to do this.
Any one able to point me in the right direction? I understand a batch should have a start, execute and finish. However i am not sure where i should split the csv and where to read and load?
I found this link which i could not work out how to translate into my requirements: http://developer.financialforce.com/customizations/importing-large-csv-files-via-batch-apex/
Here is the code i have for the standard apex class which works.
public class importOppLinesController {
public List<OpportunityLineItem> oLiObj {get;set;}
public String recOppId {
get;
// *** setter is NOT being called ***
set {
recOppId = value;
System.debug('value: '+value);
}
}
public Blob csvFileBody{get;set;}
public string csvAsString{get;set;}
public String[] csvFileLines{get;set;}
public List<OpportunityLineItem> oppLine{get;set;}
public importOppLinesController(){
csvFileLines = new String[]{};
oppLine = New List<OpportunityLineItem>();
}
public void importCSVFile(){
PricebookEntry pbeId;
String unitPrice = '';
try{
csvAsString = csvFileBody.toString();
csvFileLines = csvAsString.split('\n');
for(Integer i=1;i<csvFileLines.size();i++){
OpportunityLineItem oLiObj = new OpportunityLineItem() ;
string[] csvRecordData = csvFileLines[i].split(',');
String pbeCode = csvRecordData[0];
pbeId = [SELECT Id FROM PricebookEntry WHERE ProductCode = :pbeCode AND Pricebook2Id = 'xxxx HardCodedValue xxxx'][0];
oLiObj.PricebookEntryId = pbeId.Id;
oLiObj.Quantity = Decimal.valueOf(csvRecordData[1]) ;
unitPrice = String.valueOf(csvRecordData[2]);
oLiObj.UnitPrice = Decimal.valueOf(unitPrice);
oLiObj.OpportunityId = 'recOppId';;
insert (oLiObj);
}
}
catch (Exception e)
{
ApexPages.Message errorMessage = new ApexPages.Message(ApexPages.severity.ERROR, e + ' - ' + unitPrice);
ApexPages.addMessage(errorMessage);
}
}
}
First problem that I can sense is that the insert DML statement is inside FOR-loop. Can you put the new "oLiObj" into a List that is declared before the FOR-loop starts and then try inserting the list after the FOR-loop ?
It should bring some more sanity in your code.
I am using a SOQL like below
select COUNT(Transaction_Type_Id__c) tt, Id__c from Analysis_set_header__c group by Id__c
The Object having total 42 records.but I am getting error like
Aggregate query does not support queryMore(), use LIMIT to restrict the results to a single batch.
Here is my batch class
global class AnalysisSetWsCodeBatchClass implements Database.Batchable<sObject>, Database.AllowsCallouts {
public String query = 'select COUNT(Transaction_Type_Id__c) tt, Id__c from Analysis_set_header__c group by Id__c';
global Database.QueryLocator start(Database.BatchableContext BC) {
return Database.getQueryLocator(query);
}
global void execute(Database.BatchableContext BC, AggregateResult[] groupedResults) {
set<String> setAh = new set<String>();
for (AggregateResult ar : groupedResults) {
System.debug('--------header---' + ar.get('Id__c'));
setAh.add((string) ar.get('Id__c'));
}
system.debug('----------------setAh----------------------'+setAh);
if(!setAh.isEmpty()){
AnalysisSetCodeWsUpdate aw = new AnalysisSetCodeWsUpdate();
aw.updateAnalysisSetCode(setAh);
}
}
global void finish(Database.BatchableContext BC){
//you can also do some after-the-job-finishes work here
}
}
how many records do you have when you query SELECT ID FROM Analysis_set_header__c? More than 43? My suggestion is to change your SOQL string to the following: SELECT COUNT_DISTINCT(Transaction_Type_Id__c) tt, Id__c from Analysis_set_header__c order by COUNT_DISTINCT(Id__c)
If possible, could anyone please direct me towards the right direction? The second code (Batch Apex) is just not compiling. Currently the error is,
Error: Compile Error: Invalid type: updateContactOnEmailOptOutChangeScheduler
at line 63 column 73
But I think there are other issues to that I can't seem to make it right.
On a contact update, the trigger updates all duplicate contacts with new value of "Email Opt Out" if this field has been updated. Also, the trigger only updates duplicate contacts that have a HasOptedOutOfEmail value different from one being updated. Now my task is to convert this requirement from trigger (that was written and tested by my colleague) to Batch Apex. First is the original trigger. Second is the code I just wrote in the format of batch apex.
Original Trigger Code
trigger updateContactOnEmailOptOutChange on Contact (after update) {
//Initialize lists and maps
List<Contact> duplicateContacts = new List<Contact>();
Map<String, Contact> contactEmailMap = new Map<String, Contact>();
Map<Id, Contact> contactIdMap = new Map<Id, Contact>();
//Build a map with contacts to update. Only select the ones that have a different "Email Opt Out" value from the contact being updated.
for (Integer i = 0; i < Trigger.new.size(); i++) {
if (Trigger.old[i].HasOptedOutOfEmail != Trigger.new[i].HasOptedOutOfEmail) {
contactEmailMap.put(Trigger.old[i].email, Trigger.new[i]);
contactIdMap.put(Trigger.old[i].id, Trigger.new[i]);
}
}
//Only go through this process if "Email Opt Out" (HasOptedOutofEmail) was updated.
If (contactIdMap.size()>0) {
//Query the database and look for all contacts with a duplicate email address (same email as the contact currently being updated).
for (Contact dupContact : [SELECT Id, Name, Email, HasOptedOutOfEmail
FROM Contact
WHERE Email IN : contactEmailMap.KeySet()
AND Id NOT IN : contactIdMap.KeySet()]) {
Contact contact = contactEmailMap.get(dupContact.Email);
If (dupContact.HasOptedOutOfEmail <> contact.HasOptedOutOfEmail) {
dupContact.HasOptedOutOfEmail = contact.HasOptedOutOfEmail;
duplicateContacts.add(dupContact);
}
}
//If any duplicate contacts were found, update all duplicate contacts with the new HasOptedOutOfEmail value.
If (duplicateContacts.size()>0) update duplicateContacts;
}
}
BATCH APEX
global class updateContactOnEmailOptOutChange implements Database.Batchable<sObject>
{
global string query;
global updateContactOnEmailOptOutChange()
{
query = 'SELECT id,Name, Email, HasOptedOutofEmail from Contact where HasOptedOutofEmail=true';
}
global Database.QueryLocator start(Database.BatchableContext BC)
{
return Database.getQueryLocator(query);
}
global void execute(Database.BatchableContext BC, List <sObject> duplicateContacts)
{
Map<String, Contact> contactEmailMap = new Map<String, Contact>();
Map <Id, Contact> contactIdMap = new Map<Id, Contact>();
// Build a map with contacts to update. Only select the ones that have a different "Email Opt Out" value from the contact being updated.
if(trigger.isUpdate){
for(Integer i=0; i<Trigger.new.size();i++)
{
if(Trigger.old[i].HasOptedOutOfEmail != Trigger.new[i].HasOptedOutOfEmail)
{
contactEmailMap.put(Trigger.old[i].email, Trigger.new[i]);
contactIdMap.put(Trigger.old[i].id, Trigger.new[i]);
}
}
if(contactidMap.size()>0)
{
//Query the database and look for all contacts with a duplicate email address(same email as the contact currently being updated)
for (Contact dupContact: [SELECT Id, Name, Email, HasOptedOutofEmail
FROM Contact
WHERE Email IN: contactEmailMap.KeySet()
AND Id NOT IN: contactIdMap.KeySet()])
{
Contact contact=contactEmailMap.get(dupContact.Email);
If(dupContact.HasOptedOutOfEmail <> contact.HasOptedOutOfEmail)
{
dupContact.HasOptedOutOfEmail = contact.HasOptedOutOfEmail;
duplicateContacts.add(dupContact);
}
}
// if any duplicate contacts were found, update all duplicate contacts with the new HasOptedOutOFEmail value.
If(duplicateContacts.size<>0) update duplicateContacts;
}
}
}
//The batch process has completed successfully. Schedule next batch.
global void finish(Database.BatchableContext BC){
// //Build the system time of now + 300 seconds to schedule the batch apex.
Datetime sysTime = System.now();
sysTime = sysTime.addSeconds(300);
String chron_exp=''+sysTime.second()+''+sysTime.minute()+''+sysTime.hour()+''+sysTime.day()+''+sysTime.month()+'?'+sysTime.year();
system.debug(chron_exp);
updateContactOnEmailOptOutChangeScheduler scheduleFieldUpdate = new updateContactOnEmailOptOutChangeScheduler();
//Schedule the next job, and give it the system time so name is unique
System.schedule('New Email Update Job'+sysTime.getTime(),chron_exp,scheduleFieldUpdate);
}
}
Your batch apex is including references to the Trigger class that is only valid inside a trigger. The compile error message says there is an invalid type on line 63. Check the line numbers but that likely points to the reference to Trigger