Month: June 2017

Using SOQL for Loops

With SOQL for loop,you can include a SOQL query within a for loop. SOQL for loops iterate over all of the sObject records returned by a SOQL query.

Syntax-

for (variable : [soql_query]) {
// code_block
}

for (variable_list : [soql_query]) {
// code_block
}

Both variable and variable_list must be of the same type as the sObjects that are returned by the soql_query.

It is preferable to use the sObject list format of the SOQL for loop as the loop executes once for each batch of 200 sObjects.

Difference between SOQL For Loops  & Standard SOQL Queries

SOQL for loops differ from standard SOQL statements because of the method they use to retrieve sObjects. The standard SOQL Queries can retrieve either the count of a query or a number of object records, SOQL for loops retrieve all sObjects, using efficient chunking with calls to the query and queryMore methods of the SOAP API.

To avoid hitting the heap size limit error, you should always use a SOQL for loop to process large record set.

NOTE – Queries including an aggregate function don’t support queryMore. A run-time exception occurs if you use a query containing an aggregate function that returns more than 2,000 rows in a for loop.

SOQL For Loop Fomrats –

SOQL for loops can process records one at a time using a single sObject variable, or in batches of 200 sObjects at a time using an sObject list:

  • The single sObject format executes the for loop’s <code_block> once per sObject record. Each DML statement ends up processing only one sObject at a time.
  • The sObject list format executes the for loop’s <code_block> once per list of 200 sObjects. Each DML statement can bulk process a list of sObjects at a time.

// Create a savepoint because the data should not be committed to the database
Savepoint sp = Database.setSavepoint();

insert new Account[]{new Account(Name = 'yyy'), 
 new Account(Name = 'yyy'), 
 new Account(Name = 'yyy')};

// The single sObject format executes the for loop once per returned record
Integer i = 0;
for (Account tmp : [SELECT Id FROM Account WHERE Name = 'yyy']) {
 i++;
}
// Since there were three accounts named 'yyy' in the
// database, the loop executed three times
System.debug('Integer Value = '+i);

// The sObject list format executes the for loop once per returned batch
// of records
i = 0;
Integer j;
for (Account[] tmp : [SELECT Id FROM Account WHERE Name = 'yyy']) {
 j = tmp.size();
 i++;
}
// The list should have contained the three accounts named 'yyy'
System.debug('@@@ Value if j = '+j);
// Since a single batch can hold up to 200 records and,
// only three records should have been returned, the 
// loop should have executed only once
System.debug('### Value of i = '+i);

// Revert the database to the original state
Database.rollback(sp);

NOTE – 

DML statements can only process up to 10,000 records at a time, and sObject list for loops process records in batches of 200.

You might get a QueryException in a SOQL for loop with the message Aggregate query has too many rows for direct assignment, use FOR loop.

Example –  The query in the following SOQL for loop retrieves child contacts for a particular account. If this account contains more than 200 child contacts, the statements in the for loop cause an exception.

for (Account acct : [SELECT Id, Name, (SELECT Id, Name FROM Contacts) FROM Account                                      WHERE Id IN (‘<ID value>’)]) {                                                                                    List<Contact> contactList = acct.Contacts; // Causes an error                                                        Integer count = acct.Contacts.size(); // Causes an error
}

To avoid getting this exception, use a for loop to iterate over the child records, as follows.

for (Account acct : [SELECT Id, Name, (SELECT Id, Name FROM Contacts)
FROM Account WHERE Id IN (‘<ID value>’)]) {
Integer count=0;
for (Contact c : acct.Contacts) {
count++;
}
}

Delete & Update Related Records

Delete Record in Apex –

You can delete records using the delete statement.  Deleted records aren’t deleted permanently from Force.com, but they’re placed in the Recycle Bin for 15 days from where they can be restored.

Contact[] contactsDel = [SELECT Id FROM Contact WHERE LastName=’Smith’];
delete contactsDel;

Deleting Related Records – 

The delete operation supports cascading deletions. If you delete a parent object, you delete its children automatically.

Update Related records –

Fields on related records can’t be updated with the same call to the DML operation but it requires a separate DML call.

Suppose, when updating a contact, if you also want to update the contact’s related account, you must make two DML calls.


// Query for the contact, which has been associated with an account.
Contact queriedContact = [SELECT Account.Name
FROM Contact
WHERE FirstName = 'Mario' AND LastName='Ruiz'
LIMIT 1];

// Update the contact's phone number
queriedContact.Phone = '(415)555-1213';

// Update the related account industry
queriedContact.Account.Industry = 'Technology';

// Make two separate calls
// 1. This call is to update the contact's phone.
update queriedContact;
// 2. This call is to update the related account's Industry field.
update queriedContact.Account;

Upsert Record in Apex

Upsert Records – A list containing a mix of new and existing records, you can process insertions and updates to all records in the list by using the upsert statement. Upsert helps avoid the creation of duplicate records.

  • The upsert statement matches the sObjects with existing records by comparing values of one field.
  • You can specify a field to use for matching.
  • If you don’t specify a field name, the upsert statement uses the sObject’s ID to match the sObject with existing records in Salesforce.
  • For custom objects – specify a custom field marked as external ID.
  • For standard objects – you can specify any field that has the idLookup property set to true.

Syntax of Upsert – Using a field name

upsert  sObjectList  Account.Fields.MyExternalId;

Upsert uses the sObject record’s primary key (i.e. ID) field, an idLookup field, or an externalID field to determine whether it should create a new record or update an existing one:

  • If the key is not matched, a new object record is created.
  • If the key is matched once, the existing object record is updated.
  • If the key is matched multiple times, an error is generated and the object record is neither inserted or updated.

// Insert the Josh contact
Contact josh = new Contact(FirstName='Josh',LastName='Kaplan',Department='Finance');
insert josh;

// Josh's record has been inserted so the variable josh has now an ID
// which will be used to match the records by upsert
josh.Description = 'Josh\'s record has been updated by the upsert operation.';

// Create the Kathy contact, but don't persist it in the database
Contact kathy = new Contact(FirstName='Kathy',LastName='Brown',Department='Technology');

// List to hold the new contacts to upsert
List<Contact> contacts = new List<Contact> { josh, kathy };

// Call upsert
upsert contacts;

// <strong>Result-</strong>: Josh is updated and Kathy is created.

Note- If insert was used in this example instead of upsert, a duplicate Jane Smith contact would have been inserted.

API Field Property – 

idLookup   —  Can be used to specify a record in an upsert call. The Id field of each object has this property and some Name fields. There are exceptions, so check for the property in any object you wish to upsert.

Apex DML

DML can be perform in two ways in Apex – Using DML statements or Database class methods.

DML Statements-
  1. insert
  2. update
  3. upsert
  4. delete
  5. undelete
  6. merge

Upsert – creates new records or updates sObject records within a single statement, using a specified field to determine the presence of existing objects, or the ID field if no field is specified.

Merge – merge statement merges up to three records of the same sObject type into one of the records, deleting the others, and re-parenting any related records.

Usinig DML Statements – to insert new record

 //Create the List of sObject to insert  
 List accList = new List();  
 accList.add(new Account(Name='TestAcc1'));  
 accList.add(new Account(Name='TestAcc3'));  
 accList.add(new Account(Name='TestAcc3'));  
 // DML Statement  
 insert accList;  

Using Database class – to insert record

 //Create the List of sObject to insert  
 List accList = new List();  
 accList.add(new Account(Name='TestAcc1'));  
 accList.add(new Account(Name='TestAcc3'));  
 // DML Statement  
 Database.SaveResult[] srList = Database.insert(accList, false);  
 //Iterate through each returned result  
 for(Database.SaveResult sr : srList) {  
     if(sr.isSuccess()) {  
         // Operation was successful, so get the ID of the record that was processed  
         System.debug('Successfully inserted account. Account ID: ' + sr.getId());  
     }  
     else {  
         // Operation failed, so get all errors  
         for(Database.Error err : sr.getErrors()) {  
             System.debug('The following error has occurred.');  
             System.debug(err.getStatusCode() + ': ' + err.getMessage());  
             System.debug('Account fields that affected this error: ' + err.getFields());  
         }  
     }  
 }  

Database Methods :- Apex Database class provides methods that perform DML operations. These Database methods are static and are called using class name(Database).

  • Database.insert()
  • Database.update()
  • Database.delete()
  • Database.upsert()
  • Database.merge()
  • Database.undelete()

Using Database class method , you have an option allOrNone which specify whether to allow for partial record processing if errors are encountered.You can do so by passing an additional second Boolean parameter.

If you specify false for this parameter and if a record fails, the remainder of DML operations can still succeed. This wont throw any exception, instead return a result object array containing the status of each operation and any error occur.

By default this optional parameter is true – means that if at least one sObject failed to processed then all sObject can’t processed

Insert and Update operation return an array of Database.saveResult object.

Database.saveResult[] results = Database.insert(recordList, false);

NOTE- Upsert return Database.upsertResult object and delete returns Database.deleteResult object.

When to use DML Statement or Database class methods :-

Use DML statements if you want any error that occurs during bulk DML processing to be thrown as an Apex exception that immediately interrupts control flow (by using try. . .catch blocks). This behavior is similar to the way exceptions are handled in most database procedural languages.

Use Database class methods if you want to allow partial success of a bulk DML operation—if a record fails, the remainder of the DML operation can still succeed. Your application can then inspect the rejected records and possibly retry the operation. When using this form, you can write code that never throws DML exception errors. Instead, your code can use the appropriate results array to judge success or failure. Note that Database methods also include a syntax that supports thrown exceptions, similar to DML statements.

 Single Vs. Bulk DML :-

DML operations can performed on single sObject or in bulk on a list of sObject. Performing bulk DML operations is the recommended way because it helps avoid hitting governor limits, such as the DML limit of 150 statements per Apex transaction.

Requirement :- Updating all Contacts Description__c field to a new value if the department field matches a certain value.

Wrong approach :-

 for(Contact badCon : conList) {  
     if (badCon.Department = 'Finance') {  
         badCon.Description__c = 'New description';  
     }  
     // Not a good practice since governor limits might be hit.  
     update badCon;  
 }  

You are looping through all contact and the contact whose department matches to the value, you are updating the contact. The problem here is -If the number of contact with matching criteria is more then 150, the next update throw exception that can’t be caught for exceeding the DML statement limit of 150.

Recommended Approach :-  Bulkify DML

 // List to hold the new contacts to update.  
 List updatedList = new List();  
 for(Contact con : conList) {  
     if (con.Department == 'Finance') {  
         con.Description = 'New description';  
         // Add updated contact sObject to the list.  
         updatedList.add(con);  
     }  
 }  
 // Call update on the list of contacts.  
 // This results in one DML call for the entire list.  
 update updatedList;  

Here you are bulkifies the DML by calling update on a list of contacts and this is only one DML, below the limit 150 DML.

The other governor limit that affects DML operations is the total number of 10,000 rows that can be processed by DML operations in a single transaction.

DML Transaction:- DML operations execute within a transaction. All DML operations in a transaction either complete successfully, or if an error occurs in one operation, the entire transaction is rolled back and no data is committed to the database.

For example, if a trigger or class creates two accounts and updates one contact, and the contact update fails because of a validation rule failure, the entire transaction rolls back and none of the accounts are persisted in Salesforce.

continue

Locking Record in Salesforce

We can lock record in Salesforce to prevent user or client allow to make update either through code or UI. So lock record can’t be changed during the lock period.

Locking Statement –

In Apex, you can use FOR UPDATE to lock sObject record.

To lock a set of sObject records in Apex, embed the keywords FOR UPDATE after any inline SOQL statement. For example, You are querying for two accounts, also locks the accounts that are returned:

Account [] accts = [ SELECT Id FROM Account LIMIT 2 FOR UPDATE ];

NOTE- You can’t use the ORDER BY keywords in any SOQL query that uses locking.

Locking in a SOQL For Loop

The FOR UPDATE keywords can also be used within SOQL for loops.

for (Account[] accts : [SELECT Id FROM Account FOR UPDATE]) {
&nbsp; &nbsp; // Your code
}

Locking Consideration-
  1. While the records are locked by a client, the locking client can modify their field values in the database in the same transaction. Other clients have to wait until the transaction completes and records are unlocked .
  2. Other clients can still query the same records while they’re locked.
  3. If you try to lock a record currently locked by another client, your process waits for the lock to be released before acquiring a new lock. If the lock isn’t released within 10 seconds, you will get a QueryException.
  4. If you try to update a locked record  by another client and the lock isn’t released within 10 seconds, you will get a DmlException.
  5. If a client try to modify a locked record, the update operation might succeed if the lock gets released within a short amount of time after the update call was made.                 But there might be a chance of the second client update call data will overwrites by the old locked data from first client. To prevent this from happening, the second client must lock the record first.The locking process returns a fresh copy of the record from the database through the SELECT statement.
  6. When you perform a DML operation on one record, related records are locked in addition to the record in question.

One to One relationship in Salesforce

As of now, there is not out of box functionality available to create one-one relationship but there are way we can create one-one relationship in Salesforce.

  1. When both object are having Lookup Relationship.
  • Create a unique field on Child object.
  • Write a Workflow on Child object, for any change in the lookup field, to capture the lookup field value to unique field.

Now one-one relationship is created for both.

Disadvantage of this process is –

  • Creating an extra field.
  • Utilizing the Unique field limit.[7 External ID fields per object]
  • Using a workflow.
  1. When both object are having Master Details Relationship.
  • Create a roll-up summary field on Master to get the Count of Child.
  • Write a validation rule on rollup summary field to check greater then 1. It will give you error if it has more than one record.

Disadvantage of this Process is –

  • Feasible only if relationship is Master-Details.
  • Consuming rollup summary limit.[25 Rollup Summary Fields]

 

Maps Collection and Map of sObject

A map is a collection of key-value pairs where each unique key maps to a single value. Keys and values can be any data type—primitive types, collections, sObjects, user-defined types, and built-in Apex types.

Map keys and values can contain any collection, and can contain nested collections. For example, you can have a map of Integers to maps, which, in turn, map Strings to lists.

You can use the generic or specific sObject data types with maps.

Map Considerations –
  • Apex developers do not need to reference the algorithm that is used to implement a map in their declarations (for example, HashMap or TreeMap). Apex uses a hash structure for all maps.
  • The iteration order of map elements is deterministic.
  • A map key can hold the null value.
  • Adding a map entry with a key that matches an existing key in the map overwrites the existing entry with that key with the new entry.
  • Map keys of type String are case-sensitive.
  • Uniqueness of map keys of user-defined types is determined by the equals and hashCode methods.
  • A Map object is serializable into JSON only if it uses one of the following data types as a key.
Maps of sObject – 

Map keys and values can be of any data type, including sObject types, such as Account.

Maps can hold sObjects both in their keys and values. A map key represents a unique value that maps to a map value.

Q- Can we declare a map like below.[Interview Question]

Map<sObject, List> mapTest = new Map<sObject, List>();

Ans – Yes, you can declare with out any issue.

Maps allow sObjects in their keys. You should use sObjects in the keys only when the sObject field values won’t change.

Populating Map from SOQL query –

Maps can be directly populated from the results returned by the SOQL query. The map key should be declared with an ID or String data type, and the map value should be declared as an sObject data type.

// Populate map from SOQL query
Map<ID, Account> accMap = new Map<ID, Account>([SELECT Id, Name FROM Account LIMIT 10]);
// After populating the map, iterate through the map entries
for (ID idKey : accMap.keyset()) {
Account a = accMap.get(idKey);
System.debug(a);
}

Few Map Methods –


Account myAcct = new Account(); //Define a new account
Map<Integer, Account> m = new Map<Integer, Account>(); // Define a new map
m.put(1, myAcct); // Insert a new key-value pair in the map
System.assert(!m.containsKey(3)); // Assert that the map contains a key
Account a = m.get(1); // Retrieve a value, given a particular key
Set<Integer> s = m.keySet(); // Return a set that contains all of the keys in the map

sObject Map Considerations –
  1. Using sObject as map key.

Be careful when using sObject as map keys. Key matching for sObjects is based on the comparison of all sObject field values.

If one or more field values change after adding an sObject to the map, attempting to retrieve this sObject from the map returns null. This is because the modified sObject isn’t found in the map due to different field values.

2.  Using sObject map key field in Trigger

When using before and after insert triggers for an sObject. If those triggers share a static map defined in a class, and the sObjects in Trigger.New are added to this map in the before trigger, the sObjects in Trigger.New in the after trigger aren’t found in the map because the two sets of sObjects differ by the fields that are autofilled.

The sObjects in Trigger.New in the after trigger have system fields populated after insertion, namely: ID, CreatedDate, CreatedById, LastModifiedDate, LastModifiedById, and SystemModStamp.


// Create an account and add it to the map
Account a1 = new Account(Name='A1');

Map<sObject, Integer> m = new Map<sObject, Integer>{a1 => 1};

// Get a1's value from the map. Returns the value of 1.
System.debug('Map value='+m.get(a1));
// Id field is null, as the Account is not inserted
System.debug('Map value 2='+a1.Id);

// Insert a1.This causes the ID field on a1 to be auto-filled
insert a1;
// Id field is now populated.
System.debug('Acc value='+a1.Id);

// Get a1's value from the map again.
// Returns null because Map.get(sObject) doesn't find
// the entry based on the sObject with an auto-filled ID.
// This is because when a1 was originally added to the map
// before the insert operation, the ID of a1 was null.
System.debug('Map value again='+m.get(a1));