Apex Flashcards
Apex is
Hosted—Apex is saved, compiled, and executed on the server—the Lightning Platform.
Object oriented—Apex supports classes, interfaces, and inheritance.
Strongly typed—Apex validates references to objects at compile time.
Multitenant aware—Because Apex runs in a multitenant platform, it guards closely against runaway code by enforcing limits, which prevent code from monopolizing shared resources.
Integrated with the database—It is straightforward to access and manipulate records. Apex provides direct access to records and their fields, and provides statements and query languages to manipulate those records.
Data focused—Apex provides transactional access to the database, allowing you to roll back operations.
Easy to use—Apex is based on familiar Java idioms.
Easy to test—Apex provides built-in support for unit test creation, execution, and code coverage. Salesforce ensures that all custom Apex code works as expected by executing all unit tests prior to any platform upgrades.
Versioned—Custom Apex code can be saved against different versions of the API.
Unlike other object-oriented programming languages, Apex supports
Unlike other object-oriented programming languages, Apex supports:
- Cloud development as Apex is stored, compiled, and executed in the cloud.
- Triggers, which are similar to triggers in database systems.
- Database statements that allow you to make direct database calls and query languages to query and search data.
- Transactions and rollbacks.
- The global access modifier, which is more permissive than the public modifier and allows access across namespaces and applications.
- Versioning of custom code.
- In addition, Apex is a case-insensitive language.
Choose a Salesforce Org for Apex Development
You can develop Apex in a sandbox, scratch org, or Developer Edition org, but not directly in a production org. With so many choices, here’s some help to determine which org type is right for you and how to create it.
Sandboxes (Recommended)
A sandbox is a copy of your production org’s metadata in a separate environment, with varying amounts of data depending on the sandbox type. A sandbox provides a safe space for developers and admins to experiment with new features and validate changes before deploying code to production. Developer and Developer Pro sandboxes with source tracking enabled can take advantage of many of the features of our Salesforce DX source-driven development tools, including Salesforce CLI, Code Builder, and DevOps Center.
Scratch Orgs (Recommended)
A scratch org is a source-driven and temporary deployment of Salesforce code and metadata. A scratch org is fully configurable, allowing you to emulate different Salesforce editions with different features and settings. Scratch orgs have a maximum 30-day lifespan, with the default set at 7 days.
Developer Edition (DE) Orgs
A DE org is a free org that provides access to many of the features available in an Enterprise Edition org. Developer Edition orgs can become out-of-date over time and have limited storage. Developer Edition orgs don’t have source tracking enabled and can’t be used as development environments in DevOps Center. Developer Edition orgs expire if they aren’t logged into regularly. You can sign up for as many Developer Edition orgs as you like on the Developer Edition Signup page.
Trial Edition Orgs
Trial editions usually expire after 30 days, so they’re great for evaluating Salesforce functionality but aren’t intended for use as a permanent development environment. Although Apex triggers are available in trial editions, they’re disabled when you convert to any other edition. Deploy your code to another org before conversion to retain your Apex triggers. Salesforce offers several product- and industry-specific free trial orgs.
Production Orgs (Not Supported)
A production org is the final destination for your code and applications, and has live users accessing your data. You can’t develop Apex in your Salesforce production org, and we recommend that you avoid directly modifying any code or metadata directly in production. Live users accessing the system while you’re developing can destabilize your data or corrupt your application.
Developer Console
The Developer Console is an integrated development environment (IDE) built into Salesforce. Use it to create, debug, and test Apex classes and triggers.
To open the Developer Console from Lightning Experience: Click the quick access menu (Gear icon in upper right of Salesforce org), then click Developer Console.
To open the Developer Console from Salesforce Classic: Click Your Name | Developer Console.
The Developer Console supports these tasks:
- Writing code—You can add code using the source code editor. Also, you can browse packages in your organization.
- Compiling code—When you save a trigger or class, the code is automatically compiled. Any compilation errors are reported.
- Debugging—You can view debug logs and set checkpoints that aid in debugging.
- Testing—You can execute tests of specific test classes or all tests in your organization, and you can view test results. Also, you can inspect code coverage.
- Checking performance—You can inspect debug logs to locate performance bottlenecks.
- SOQL queries—You can query data in your organization and view the results using the Query Editor.
- Color coding and autocomplete—The source code editor uses a color scheme for easier readability of code elements and provides autocompletion for class and method names.
Data Types
In Apex, all variables and expressions have a data type, such as sObject, primitive, or enum.
1. A primitive, such as an Integer, Double, Long, Date, Datetime, String, ID, or Boolean (see Primitive Data Types)
2. An sObject, either as a generic sObject or as a specific sObject, such as an Account, Contact, or MyCustomObject_c (see Working with sObjects in Chapter 4.)
A collection, including:
3. A list (or array) of primitives, sObjects, user defined objects, objects created from Apex classes, or collections (see Lists)
4. A set of primitives (see Sets)
5. A map from a primitive to a primitive, sObject, or collection (see Maps)
6. A typed list of values, also known as an enum (see Enums)
7. Objects created from user-defined Apex classes (see Classes, Objects, and Interfaces)
8. Objects created from system supplied Apex classes
9. Null (for the null constant, which can be assigned to any variable)
Methods can return values of any of the listed types, or return no value and be of type Void.
Type checking is strictly enforced at compile time.
sObject Types
Account a = new Account(); MyCustomObjectc co = new MyCustomObject\_\_c(); // Cast the generic variable s from the example above // into a specific account and account variable a Account a = (Account) s; // The following generates a runtime error Contact c = (Contact) s;
Custom Labels
Custom labels aren’t standard sObjects. You can’t create a new instance of a custom label. You can only access the value of a custom label using system.label.label_name. For example:
String errorMsg = System.Label.genericerror;
The following example shows how you can use SOSL over a set of records to determine their object types. Once you have converted the generic SObject record into a Contact, Lead, or Account, you can modify its fields accordingly
public class convertToCLA { List<Contact> contacts = new List<Contact>(); List<Lead> leads = new List<Lead>(); List<Account> accounts = new List<Account>(); public void convertType(String phoneNumber) { List<List<SObject>> results = [FIND :phoneNumber IN Phone FIELDS RETURNING Contact(Id, Phone, FirstName, LastName), Lead(Id, Phone, FirstName, LastName), Account(Id, Phone, Name)]; List<SObject> records = new List<SObject>(); records.addAll(results[0]); //add Contact results to our results super-set records.addAll(results[1]); //add Lead results records.addAll(results[2]); //add Account results if (!records.isEmpty()) { for (Integer i = 0; i < records.size(); i++) { SObject record = records[i]; if (record.getSObjectType() == Contact.sObjectType) { contacts.add((Contact) record); } else if (record.getSObjectType() == Lead.sObjectType){ leads.add((Lead) record); } else if (record.getSObjectType() == Account.sObjectType) { accounts.add((Account) record); } } } } }
Using SObject Fields
SObject fields can be initially set or not set (unset); unset fields are not the same as null or blank fields. When you perform a DML operation on an SObject, you can change a field that is set; you can’t change unset fields.
Note
To erase the current value of a field, set the field to null.
If an Apex method takes an SObject parameter, you can use the System.isSet() method to identify the set fields. If you want to unset any fields to retain their values, first create an SObject instance. Then apply only the fields you want to be part of the DML operation.
An expression with SObject fields of type Boolean evaluates to true only if the SObject field is true. If the field is false or null, the expression evaluates to false.
This example code shows an expression that checks if the IsActive field of a Campaign object is null. Because this expression always evaluates to false, the code in the if statement is never executed.
Campaign cObj= new Campaign(); ... if (cObj.IsActive == null) { ... // IsActive is evaluated to false and this code block is not executed. }
Single vs. Bulk DML Operations
You can perform DML operations either on a single sObject, or in bulk on a list of sObjects. 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.
Another DML governor limit is the total number of rows that can be processed by DML operations in a single transaction, which is 10,000. All rows processed by all DML calls in the same transaction count incrementally toward this limit. For example, if you insert 100 contacts and update 50 contacts in the same transaction, your total DML processed rows are 150. You still have 9,850 rows left (10,000 - 150).
System Context and Sharing Rules
Most DML operations execute in system context, ignoring the current user’s permissions, field-level security, organization-wide defaults, position in the role hierarchy, and sharing rules.
Note
If you execute DML operations within an anonymous block, they execute using the current user’s object and field-level permissions.
DML Operations, except create
Among the operations you can perform are record updates, deletions, restoring records from the Recycle Bin, merging records, or converting leads. After querying for records, you get sObject instances that you can modify and then persist the changes of.
DML Statements vs. Database Class Methods
Apex offers two ways to perform DML operations: using DML statements or Database class methods. This provides flexibility in how you perform data operations. DML statements are more straightforward to use and result in exceptions that you can handle in your code.
One difference between the two options is that by using the Database class method, you can specify whether or not 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. Also, instead of exceptions, a result object array (or one result object if only one sObject was passed in) is returned containing the status of each operation and any errors encountered. By default, this optional parameter is true, which means that if at least one sObject can’t be processed, all remaining sObjects won’t and an exception will be thrown for the record that causes a failure.
The following helps you decide when you want to use DML statements 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.
Most operations overlap between the two, except for a few.
The convertLead operation is only available as a Database class method, not as a DML statement.
The Database class also provides methods not available as DML statements, such as methods transaction control and rollback, emptying the Recycle Bin, and methods related to SOQL queries.
DML Operations As Atomic Transactions
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. The boundary of a transaction can be a trigger, a class method, an anonymous block of code, an Apex page, or a custom Web service method.
All operations that occur inside the transaction boundary represent a single unit of operations. This also applies to calls that are made from the transaction boundary to external code, such as classes or triggers that get fired as a result of the code running in the transaction boundary. For example, consider the following chain of operations: a custom Apex Web service method calls a method in a class that performs some DML operations. In this case, all changes are committed to the database only after all operations in the transaction finish executing and don’t cause any errors. If an error occurs in any of the intermediate steps, all database changes are rolled back and the transaction isn’t committed.
DML: Inserting and Updating Records
Account[] accts = new List<Account>(); for(Integer i=0;i<3;i++) { Account a = new Account(Name='Acme' + i, BillingCity='San Francisco'); accts.add(a); } Account accountToUpdate; try { insert accts; // Update account Acme2. accountToUpdate = [SELECT BillingCity FROM Account WHERE Name='Acme2' AND BillingCity='San Francisco' LIMIT 1]; // Update the billing city. accountToUpdate.BillingCity = 'New York'; // Make the update call. update accountToUpdate; } catch(DmlException e) { System.debug('An unexpected error has occurred: ' + e.getMessage()); } // Verify that the billing city was updated to New York. Account afterUpdate = [SELECT BillingCity FROM Account WHERE Id=:accountToUpdate.Id]; System.assertEquals('New York', afterUpdate.BillingCity);
DML: Inserting Related Records
You can insert records related to existing records if a relationship has already been defined between the two objects, such as a lookup or master-detail relationship. A record is associated with a related record through a foreign key ID. For example, when inserting a new contact, you can specify the contact’s related account record by setting the value of the AccountId field.
~~~
try {
Account acct = new Account(Name=’SFDC Account’);
insert acct;
// Once the account is inserted, the sObject will be // populated with an ID. // Get this ID. ID acctID = acct.ID; // Add a contact to this account. Contact con = new Contact( FirstName='Joe', LastName='Smith', Phone='415.555.1212', AccountId=acctID); insert con; } catch(DmlException e) { System.debug('An unexpected error has occurred: ' + e.getMessage()); } ~~~
DML: Updating Related Records
Fields on related records can’t be updated with the same call to the DML operation and require a separate DML call. For example, if inserting a new contact, you can specify the contact’s related account record by setting the value of the AccountId field. However, you can’t change the account’s name without updating the account itself with a separate DML call. Similarly, when updating a contact, if you also want to update the contact’s related account, you must make two DML calls. The following example updates a contact and its related account using two update statements.
DML: Relating Records by Using an External ID
Add related records by using a custom external ID field on the parent record. Associating records through the external ID field is an alternative to using the record ID. You can add a related record to another record only if a relationship (such as master-detail or lookup) has been defined for the objects involved.
This example relates a new opportunity to an existing account. The Account sObject has a custom field marked as External ID. An opportunity record is associated to the account record through the custom External ID field. The example assumes that:
The Account sObject has an external ID field of type text and named MyExtID
An account record exists where MyExtID_c = ‘SAP111111’
Before the new opportunity is inserted, the account record is added to this opportunity as an sObject through the Opportunity.Account relationship field.
~~~
Opportunity newOpportunity = new Opportunity(
Name=’OpportunityWithAccountInsert’,
StageName=’Prospecting’,
CloseDate=Date.today().addDays(7));
// Create the parent record reference.
// An account with external ID = ‘SAP111111’ already exists.
// This sObject is used only for foreign key reference
// and doesn’t contain any other fields.
Account accountReference = new Account(
MyExtID__c=’SAP111111’);
// Add the account sObject to the opportunity.
newOpportunity.Account = accountReference;
// Create the opportunity.
Database.SaveResult results = Database.insert(newOpportunity);
~~~
DML: Creating Parent and Child Records in a Single Statement Using Foreign Keys
You can use external ID fields as foreign keys to create parent and child records of different sObject types in a single step instead of creating the parent record first, querying its ID, and then creating the child record. To do this:
Create the child sObject and populate its required fields, and optionally other fields.
Create the parent reference sObject used only for setting the parent foreign key reference on the child sObject. This sObject has only the external ID field defined and no other fields set.
Set the foreign key field of the child sObject to the parent reference sObject you just created.
Create another parent sObject to be passed to the insert statement. This sObject must have the required fields (and optionally other fields) set in addition to the external ID field.
Call insert by passing it an array of sObjects to create. The parent sObject must precede the child sObject in the array, that is, the array index of the parent must be lower than the child’s index.
You can create related records that are up to 10 levels deep. Also, the related records created in a single call must have different sObject types. For more information, see Creating Records for Different Object Types in the SOAP API Developer Guide.
The following example shows how to create an opportunity with a parent account using the same insert statement. The example creates an Opportunity sObject and populates some of its fields, then creates two Account objects. The first account is only for the foreign key relationship, and the second is for the account creation and has the account fields set. Both accounts have the external ID field, MyExtID_c, set. Next, the sample calls Database.insert by passing it an array of sObjects. The first element in the array is the parent sObject and the second is the opportunity sObject. The Database.insert statement creates the opportunity with its parent account in a single step. Finally, the sample checks the results and writes the IDs of the created records to the debug log, or the first error if record creation fails. This sample requires an external ID text field on Account called MyExtID.
public class ParentChildSample { public static void InsertParentChild() { Date dt = Date.today(); dt = dt.addDays(7); Opportunity newOpportunity = new Opportunity( Name='OpportunityWithAccountInsert', StageName='Prospecting', CloseDate=dt); // Create the parent reference. // Used only for foreign key reference // and doesn't contain any other fields. Account accountReference = new Account( MyExtID\_\_c='SAP111111'); newOpportunity.Account = accountReference; // Create the Account object to insert. // Same as above but has Name field. // Used for the insert. Account parentAccount = new Account( Name='Hallie', MyExtID\_\_c='SAP111111'); // Create the account and the opportunity. Database.SaveResult[] results = Database.insert(new SObject[] { parentAccount, newOpportunity }); // Check results. for (Integer i = 0; i < results.size(); i++) { if (results[i].isSuccess()) { System.debug('Successfully created ID: ' \+ results[i].getId()); } else { System.debug('Error: could not create sobject ' \+ 'for array element ' + i + '.'); System.debug(' The error reported was: ' \+ results[i].getErrors()[0].getMessage() + '\n'); } } } }
DML: Upserting Records
Using the upsert operation, you can either insert or update an existing record in one call. To determine whether a record already exists, the upsert statement or Database method uses the record’s ID as the key to match records, a custom external ID field, or a standard field with the idLookup attribute set to true.
* If the key isn’t matched, then a new object record is created.
* If the key is matched once, then the existing object record is updated.
* If the key is matched multiple times, then an error is generated and the object record is not inserted or updated.
Note
Custom field matching is case-insensitive only if the custom field has the Unique and Treat “ABC” and “abc” as duplicate values (case insensitive) attributes selected as part of the field definition. If this is the case, “ABC123” is matched with “abc123.”
Account[] acctsList = [SELECT Id, Name, BillingCity FROM Account WHERE BillingCity = 'Bombay']; for (Account a : acctsList) { a.BillingCity = 'Mumbai'; } Account newAcct = new Account(Name = 'Acme', BillingCity = 'San Francisco'); acctsList.add(newAcct); try { upsert acctsList; } catch (DmlException e) { // Process exception here }
Use of upsert with an external ID can reduce the number of DML statements in your code, and help you to avoid hitting governor limits (see Execution Governors and Limits).
This example uses upsert and an external ID field Line_Item_Id_c on the Asset object to maintain a one-to-one relationship between an asset and an opportunity line item. Before running the sample, create a custom text field on the Asset object named Line_Item_Id_c and mark it as an external ID.
Note
External ID fields used in upsert calls must be unique or the user must have the View All Data permission.
public void upsertExample() { Opportunity opp = [SELECT Id, Name, AccountId, (SELECT Id, PricebookEntry.Product2Id, PricebookEntry.Name FROM OpportunityLineItems) FROM Opportunity WHERE HasOpportunityLineItem = true LIMIT 1]; Asset[] assets = new Asset[]{}; // Create an asset for each line item on the opportunity for (OpportunityLineItem lineItem:opp.OpportunityLineItems) { //This code populates the line item Id, AccountId, and Product2Id for each asset Asset asset = new Asset(Name = lineItem.PricebookEntry.Name, LineItemIDc = lineItem.Id, AccountId = opp.AccountId, Product2Id = lineItem.PricebookEntry.Product2Id); assets.add(asset); } try { upsert assets LineItemIDc; // This line upserts the assets list with // the LineItemIdc field specified as the // Asset field that should be used for matching // the record that should be upserted. } catch (DmlException e) { System.debug(e.getMessage()); } }
DML: Merging Records
When you have duplicate lead, contact, case, or account records in the database, cleaning up your data and consolidating the records might be a good idea. You can merge up to three records of the same sObject type. The merge operation merges up to three records into one of the records, deletes the others, and reparents any related records.
Merge Considerations
When merging sObject records, consider the following rules and guidelines:
1. Only leads, contacts, cases, and accounts can be merged. See sObjects That Don’t Support DML Operations.
2. You can pass a master record and up to two additional sObject records to a single merge method.
3. Using the Apex merge operation, field values on the master record always supersede the corresponding field values on the records to be merged. To preserve a merged record field value, simply set this field value on the master sObject before performing the merge.
4. External ID fields can’t be used with merge.
For more information on merging leads, contacts and accounts, see the Salesforce online help.
Example
The following shows how to merge an existing Account record into a master account. The account to merge has a related contact, which is moved to the master account record after the merge operation. Also, after merging, the merge record is deleted and only one record remains in the database. This examples starts by creating a list of two accounts and inserts the list. Then it executes queries to get the new account records from the database, and adds a contact to the account to be merged. Next, it merges the two accounts. Finally, it verifies that the contact has been moved to the master account and the second account has been deleted.
// Insert new accounts List<Account> ls = new List<Account>{ new Account(name='Acme Inc.'), new Account(name='Acme') }; insert ls; // Queries to get the inserted accounts Account masterAcct = [SELECT Id, Name FROM Account WHERE Name = 'Acme Inc.' LIMIT 1]; Account mergeAcct = [SELECT Id, Name FROM Account WHERE Name = 'Acme' LIMIT 1]; // Add a contact to the account to be merged Contact c = new Contact(FirstName='Joe',LastName='Merged'); c.AccountId = mergeAcct.Id; insert c; try { merge masterAcct mergeAcct; } catch (DmlException e) { // Process exception System.debug('An unexpected error has occurred: ' + e.getMessage()); } // Once the account is merged with the master account, // the related contact should be moved to the master record. masterAcct = [SELECT Id, Name, (SELECT FirstName,LastName From Contacts) FROM Account WHERE Name = 'Acme Inc.' LIMIT 1]; System.assert(masterAcct.getSObjects('Contacts').size() > 0); System.assertEquals('Joe', masterAcct.getSObjects('Contacts')[0].get('FirstName')); System.assertEquals('Merged', masterAcct.getSObjects('Contacts')[0].get('LastName')); // Verify that the merge record got deleted Account[] result = [SELECT Id, Name FROM Account WHERE Id=:mergeAcct.Id]; System.assertEquals(0, result.size());
DML: Deleting Records
After you persist records in the database, you can delete those records using the delete operation. Deleted records aren’t deleted permanently from Salesforce, but they are placed in the Recycle Bin for 15 days from where they can be restored.
~~~
Account[] doomedAccts = [SELECT Id, Name FROM Account
WHERE Name = ‘DotCom’];
try {
delete doomedAccts;
} catch (DmlException e) {
// Process exception here
}
~~~
DML: Referential Integrity When Deleting and Restoring Records
The delete operation supports cascading deletions. If you delete a parent object, you delete its children automatically, as long as each child record can be deleted.
For example, if you delete a case record, Apex automatically deletes any CaseComment, CaseHistory, and CaseSolution records associated with that case. However, if a particular child record is not deletable or is currently being used, then the delete operation on the parent case record fails.
The undelete operation restores the record associations for the following types of relationships:
1. Parent accounts (as specified in the Parent Account field on an account)
1. Indirect account-contact relationships (as specified on the Related Accounts related list on a contact or the Related Contacts related list on an account)
1. Parent cases (as specified in the Parent Case field on a case)
1. Master solutions for translated solutions (as specified in the Master Solution field on a solution)
1. Managers of contacts (as specified in the Reports To field on a contact)
1. Products related to assets (as specified in the Product field on an asset)
1. Opportunities related to quotes (as specified in the Opportunity field on a quote)
1. All custom lookup relationships
1. Relationship group members on accounts and relationship groups, with some exceptions
1. Tags
1. An article’s categories, publication state, and assignments
DML: Restoring Deleted Records
After you have deleted records, the records are placed in the Recycle Bin for 15 days, after which they are permanently deleted. While the records are still in the Recycle Bin, you can restore them using the undelete operation. If you accidentally deleted some records that you want to keep, restore them from the Recycle Bin.
Account a = new Account(Name='Universal Containers'); insert(a); insert(new Contact(LastName='Carter',AccountId=a.Id)); delete a; Account[] savedAccts = [SELECT Id, Name FROM Account WHERE Name = 'Universal Containers' ALL ROWS]; undelete savedAccts;
Undelete Considerations
1. You can undelete records that were deleted as the result of a merge. However, the merge reparents the child objects, and that reparenting can’t be undone.
2. To identify deleted records, including records deleted as a result of a merge, use the ALL ROWS parameters with a SOQL query.
Database Class: Converting Leads
The convertLead DML operation converts a lead into an account and contact, as well as (optionally) an opportunity. convertLead is available only as a method on the Database class; it is not available as a DML statement.
Converting leads involves the following basic steps:
1. Your application determines the IDs of any lead(s) to be converted.
2. Optionally, your application determines the IDs of any account(s) into which to merge the lead. Your application can use SOQL to search for accounts that match the lead name.
3. Optionally, your application determines the IDs of the contact or contacts into which to merge the lead. The application can use SOQL to search for contacts that match the lead contact name.
4. Optionally, the application determines whether opportunities should be created from the leads.
5. The application uses the query (SELECT … FROM LeadStatus WHERE IsConverted=true) to obtain the leads with converted status.
6. The application calls convertLead.
7. The application iterates through the returned result or results and examines each LeadConvertResult object to determine whether conversion succeeded for each lead.
8. Optionally, when converting leads owned by a queue, the owner must be specified. This is because accounts and contacts can’t be owned by a queue. Even if you are specifying an existing account or contact, you must still specify an owner.
Example
This example shows how to use the Database.convertLead method to convert a lead. It inserts a new lead, creates a LeadConvert object, sets its status to converted, and then passes it to the Database.convertLead method. Finally, it verifies that the conversion was successful.
Lead myLead = new Lead(LastName = ‘Fry’, Company=’Fry And Sons’);
insert myLead;
Database.LeadConvert lc = new database.LeadConvert();
lc.setLeadId(myLead.id);
LeadStatus convertStatus = [SELECT Id, ApiName FROM LeadStatus WHERE IsConverted=true LIMIT 1];
lc.setConvertedStatus(convertStatus.ApiName);
Database.LeadConvertResult lcr = Database.convertLead(lc);
System.assert(lcr.isSuccess());
Convert Leads Considerations
1. Field mappings: The system automatically maps standard lead fields to standard account, contact, and opportunity fields. For custom lead fields, your Salesforce administrator can specify how they map to custom account, contact, and opportunity fields. For more information about field mappings, see Salesforce Help.
2. Merged fields: If data is merged into existing account and contact objects, only empty fields in the target object are overwritten—existing data (including IDs) are not overwritten. The only exception is if you specify setOverwriteLeadSource on the LeadConvert object to true, in which case the LeadSource field in the target contact object is overwritten with the contents of the LeadSource field in the source LeadConvert object.
3. Record types: If the organization uses record types, the default record type of the new owner is assigned to records created during lead conversion. The default record type of the user converting the lead determines the lead source values available during conversion. If the desired lead source values are not available, add the values to the default record type of the user converting the lead. For more information about record types, see Salesforce Help.
4. Picklist values: The system assigns the default picklist values for the account, contact, and opportunity when mapping any standard lead picklist fields that are blank. If your organization uses record types, blank values are replaced with the default picklist values of the new record owner.
5. Automatic feed subscriptions: When you convert a lead into a new account, contact, and opportunity, the lead owner is unsubscribed from the lead record’s Chatter feed. The lead owner, the owner of the generated records, and users that were subscribed to the lead aren’t automatically subscribed to the generated records, unless they have automatic subscriptions enabled in their Chatter feed settings. They must have automatic subscriptions enabled to see changes to the account, contact, and opportunity records in their news feed. To subscribe to records they create, users must enable the Automatically follow records that I create option in their personal settings. A user can subscribe to a record so that changes to the record display in the news feed on the user’s home page. This is a useful way to stay up-to-date with changes to records in Salesforce.
Database Class Method Result Objects
Database class methods return the results of the data operation. These result objects contain useful information about the data operation for each record, such as whether the operation was successful or not, and any error information. Each type of operation returns a specific result object type, as outlined below.
Operation Result Class
insert, update SaveResult Class
upsert UpsertResult Class
merge MergeResult Class
delete DeleteResult Class
undelete UndeleteResult Class
convertLead LeadConvertResult Class
emptyRecycleBin EmptyRecycleBinResult Class
Returned Database Errors
While DML statements always return exceptions when an operation fails for one of the records being processed and the operation is rolled back for all records, Database class methods can either do so or allow partial success for record processing. In the latter case of partial processing, Database class methods don’t throw exceptions. Instead, they return a list of errors for any errors that occurred on failed records.
The errors provide details about the failures and are contained in the result of the Database class method. For example, a SaveResult object is returned for insert and update operations. Like all returned results, SaveResult contains a method called getErrors that returns a list of Database.Error objects, representing the errors encountered, if any.
Setting DML Options
You can specify DML options for insert and update operations by setting the desired options in the Database.DMLOptions object. You can set Database.DMLOptions for the operation by calling the setOptions method on the sObject, or by passing it as a parameter to the Database.insert and Database.update methods.
Using DML options, you can specify:
The truncation behavior of fields.
Assignment rule information.
Duplicate rule information.
Whether automatic emails are sent.
The user locale for labels.
Whether the operation allows for partial success.
The Database.DMLOptions class has the following properties:
allowFieldTruncation Property
assignmentRuleHeader Property
duplicateRuleHeader
emailHeader Property
localeOptions Property
optAllOrNone Property
DMLOptions is only available for Apex saved against API versions 15.0 and higher. DMLOptions settings take effect only for record operations performed using Apex DML and not through the Salesforce user interface.
Database.DMLOptions dmo = new Database.DMLOptions(); dmo.assignmentRuleHeader.useDefaultRule= true; dmo.allowFieldTruncation = true; Lead l = new Lead(company='ABC', lastname='Smith'); l.setOptions(dmo); insert l;
Setting DML Options: Examples
allowFieldTruncation - specifies the truncation behavior of strings. In Apex saved against API versions previous to 15.0, if you specify a value for a string and that value is too large, the value is truncated. For API version 15.0 and later, if a value is specified that is too large, the operation fails and an error message is returned. The allowFieldTruncation property allows you to specify that the previous behavior, truncation, be used instead of the new behavior in Apex saved against API versions 15.0 and later.
assignmentRuleHeader - The assignmentRuleHeader property specifies the assignment rule to be used when creating a case or lead (NOT Account)
duplicateRuleHeader - The duplicateRuleHeader property determines whether a record that’s identified as a duplicate can be saved.
emailHeader - The Salesforce user interface allows you to specify whether or not to send an email when the following events occur:
Creation of a new case or task
Conversion of a case email to a contact
New user email notification
Lead queue email notification
Password reset
In Apex saved against API version 15.0 or later, the Database.DMLOptions emailHeader property enables you to specify additional information regarding the email that gets sent when one of the events occurs because of Apex DML code execution.
Using the emailHeader property, you can set these options.
triggerAutoResponseEmail: Indicates whether to trigger auto-response rules (true) or not (false), for leads and cases. This email can be automatically triggered by a number of events, for example when creating a case or resetting a user password. If this value is set to true, when a case is created, if there is an email address for the contact specified in ContactID, the email is sent to that address. If not, the email is sent to the address specified in SuppliedEmail.
triggerOtherEmail: Indicates whether to trigger email outside the organization (true) or not (false).
triggerUserEmail: Indicates whether to trigger email that is sent to users in the organization (true) or not (false).
localeOptions - The localeOptions property specifies the language of any labels that are returned by Apex. The value must be a valid user locale (language and country), such as de_DE or en_GB. The value is a String, 2-5 characters long. The first two characters are always an ISO language code, for example ‘fr’ or ‘en.’ If the value is further qualified by a country, then the string also has an underscore (_) and another ISO country code, for example ‘US’ or ‘UK.’ For example, the string for the United States is ‘en_US’, and the string for French Canadian is ‘fr_CA’.
optAllOrNone - The optAllOrNone property specifies whether the operation allows for partial success. If optAllOrNone is set to true, all changes are rolled back if any record causes errors. The default for this property is false and successfully processed records are committed while records with errors aren’t. This property is available in Apex saved against Salesforce API version 20.0 and later.
DML: Transaction Control
All requests are delimited by the trigger, class method, Web Service, Visualforce page, or anonymous block that executes the Apex code. If the entire request completes successfully, all changes are committed to the database. For example, suppose a Visualforce page called an Apex controller, which in turn called an additional Apex class. Only when all the Apex code has finished running and the Visualforce page has finished running, are the changes committed to the database. If the request doesn’t complete successfully, all database changes are rolled back.
DML: Generating Savepoints and Rolling Back Transactions
Sometimes during the processing of records, your business rules require that partial work (already executed DML statements) is rolled back so that the processing can continue in another direction. Apex gives you the ability to generate a savepoint, that is, a point in the request that specifies the state of the database at that time. Any DML statement that occurs after the savepoint can be discarded, restoring the database to the condition it was in when you generated the savepoint. All table and row locks acquired since the savepoint are released.
The following limitations apply to generating savepoint variables and rolling back the database:
If you set more than one savepoint, then roll back to a savepoint that isn’t the last savepoint you generated, the later savepoint variable is also rolled back and becomes invalid. For example, if you generated savepoint SP1 first, savepoint SP2 after that, and then you rolled back to SP1, the variable SP2 is no longer valid. If you try to use savepoint SP2, you receive a runtime error.
References to savepoints can’t cross-trigger invocations because each trigger invocation is a new trigger context. If you declare a savepoint as a static variable then try to use it across trigger contexts, you receive a run-time error.
Each savepoint you set counts against the governor limit for DML statements.
Static variables aren’t reverted during a rollback. If you try to run the trigger again, the static variables retain the values from the first run.
Database.rollback(Savepoint) and Database.setSavepoint()don’t count against the DML row limit, but count toward the DML statement limit. This behavior applies to all API versions.
The ID on an sObject inserted after setting a savepoint isn’t cleared after a rollback. Attempting to insert the sObject using the variable created before the rollback fails because the sObject variable has an ID. Updating or upserting the sObject using the same variable also fails because the sObject isn’t in the database and, thus, can’t be updated. To perform further DML operations, create an sObject variable without setting its ID.
The following is an example using the setSavepoint and rollback Database methods.
~~~
Account a = new Account(Name = ‘xyz’);
insert a;
Assert.isNull([SELECT AccountNumber FROM Account WHERE Id = :a.Id]. AccountNumber);
// Create a savepoint while AccountNumber is null
Savepoint sp = Database.setSavepoint();
// Change the account number
a.AccountNumber = ‘123’;
update a;
Assert.areEqual(‘123’, [SELECT AccountNumber FROM Account WHERE Id = :a.Id]. AccountNumber);
// Rollback to the previous null value
Database.rollback(sp);
Assert.isNull([SELECT AccountNumber FROM Account WHERE Id = :a.Id]. AccountNumber);
~~~
DML: Releasing Savepoints and Using Callouts
To allow callouts, roll back all uncommitted DML by using a savepoint. Then use the Database.releaseSavepoint method to explicitly release savepoints before making the desired callout. When Database.releaseSavepoint() is called, SAVEPOINT_RELEASE is logged.
In this example, the makeACallout() callout succeeds because the uncommitted DML is rolled back and the savepoint is released.
Savepoint sp = Database.setSavepoint();
try {
// Try a database operation
insert new Account(name=’Foo’);
integer bang = 1 / 0;
} catch (Exception ex) {
Database.rollback(sp);
Database.releaseSavepoint(sp);
makeACallout();
}
In this example, DML is pending when the callout is made. The CalloutException informs you that you must roll back the transaction before the callout is made or the transaction must be committed.
Savepoint sp = Database.setSavepoint();
insert new Account(name=’Foo’);
Database.releaseSavepoint(sp);
try {
makeACallout();
} catch (System.CalloutException ex) {
Assert.isTrue(ex.getMessage().contains(‘You have uncommitted work pending. Please commit or rollback before calling out.’));
}
Use these guidelines for using callouts and savepoints.
If there’s uncommitted work pending when Database.releaseSavepoint() is called, the uncommitted work isn’t rolled back. It’s committed if the transaction succeeds.
Attempts to roll back to a released savepoint result in a TypeException.
Attempts to roll back after calling Database.releaseSavepoint() result in a System.InvalidOperationException.
Calling the Database.releaseSavepoint() method on a savepoint also releases nested savepoints, that is, any subsequent savepoints created after a savepoint.
For Apex tests with API version 60.0 or later, all savepoints are released when Test.startTest() and Test.stopTest() are called. If any savepoints are reset, a SAVEPOINT_RESET event is logged.
Before API version 60.0, making a callout after creating savepoints throws a CalloutException regardless of whether there was uncommitted DML or the changes were rolled back to a savepoint. Also, before API version 60.0, both Database.rollback(databaseSavepoint) and Database.setSavepoint() calls incremented the DML row usage limit.
sObjects That Can’t Be Used Together in DML Operations
DML operations on certain sObjects, sometimes referred to as setup objects, can’t be mixed with DML on non-setup sObjects in the same transaction. This restriction exists because some sObjects affect the user’s access to records in the org. You must insert or update these types of sObjects in a different transaction to prevent operations from happening with incorrect access-level permissions. For example, you can’t update an account and a user role in a single transaction.
Don’t include more than one of these sObjects in the same transaction when performing DML operations or when using the Metadata API. These sObjects also can’t be used with the @IsTest (IsParellel=true) annotation. Split such operations into separate transactions.
* AuthSession
* FieldPermissions
* ForecastingShare
* Group - You can only insert and update a group in a transaction with other sObjects. Other DML operations aren’t allowed.
* GroupMember
* ObjectPermissions
* ObjectTerritory2AssignmentRule
* ObjectTerritory2AssignmentRuleItem
* PermissionSet
* PermissionSetAssignment
* QueueSObject
* RuleTerritory2Association
* SetupEntityAccess
* Territory
* Territory2
* Territory2Model
* User
If you’re using a Visualforce page with a custom controller, you can’t mix sObject types with any of these special sObjects within a single request or action. However, you can perform DML operations on these different types of sObjects in subsequent requests. For example, you can create an account with a save button, and then create a user with a non-null role with a submit button.
You can perform DML operations on more than one type of sObject in a single class using the following process:
Create a method that performs a DML operation on one type of sObject.
Create a second method that uses the future annotation to manipulate a second sObject type.
This example shows how to perform mixed DML operations by using a future method to perform a DML operation on the User object.
public class MixedDMLFuture { public static void useFutureMethod() { // First DML operation Account a = new Account(Name='Acme'); insert a; // This next operation (insert a user with a role) // can't be mixed with the previous insert unless // it is within a future method. // Call future method to insert a user with a role. Util.insertUserWithRole( 'mruiz@awcomputing.com', 'mruiz', 'mruiz@awcomputing.com', 'Ruiz'); } } public class Util { @future public static void insertUserWithRole( String uname, String al, String em, String lname) { Profile p = [SELECT Id FROM Profile WHERE Name='Standard User']; UserRole r = [SELECT Id FROM UserRole WHERE Name='COO']; // Create new user with a non-null user role ID User u = new User(alias = al, email=em, emailencodingkey='UTF-8', lastname=lname, languagelocalekey='enUS', localesidkey='enUS', profileid = p.Id, userroleid = r.Id, timezonesidkey='America/LosAngeles', username=uname); insert u; } }
Mixed DML Operations in Test Methods
Test methods allow for performing mixed Data Manipulation Language (DML) operations that include both setup sObjects and other sObjects if the code that performs the DML operations is enclosed within System.runAs method blocks. You can also perform DML in an asynchronous job that your test method calls. These techniques enable you, for example, to create a user with a role and other sObjects in the same test.
Example: Mixed DML Operations in System.runAs Blocks
~~~
@isTest
private class MixedDML {
static testMethod void mixedDMLExample() {
User u;
Account a;
User thisUser = [SELECT Id FROM User WHERE Id = :UserInfo.getUserId()];
// Insert account as current user
System.runAs (thisUser) {
Profile p = [SELECT Id FROM Profile WHERE Name=’Standard User’];
UserRole r = [SELECT Id FROM UserRole WHERE Name=’COO’];
u = new User(alias = ‘jsmith’, email=’jsmith@acme.com’,
emailencodingkey=’UTF-8’, lastname=’Smith’,
languagelocalekey=’en_US’,
localesidkey=’en_US’, profileid = p.Id, userroleid = r.Id,
timezonesidkey=’America/Los_Angeles’,
username=’jsmith@acme.com’);
insert u;
a = new Account(name=’Acme’);
insert a;
}
}
}
~~~
Use @future to Bypass the Mixed DML Error in a Test Method
Mixed DML operations within a single transaction aren’t allowed. You can’t perform DML on a setup sObject and another sObject in the same transaction. However, you can perform one type of DML as part of an asynchronous job and the others in other asynchronous jobs or in the original transaction. This class contains an @future method to be called by the class in the subsequent example.
~~~
public class InsertFutureUser {
@future
public static void insertUser() {
Profile p = [SELECT Id FROM Profile WHERE Name=’Standard User’];
UserRole r = [SELECT Id FROM UserRole WHERE Name=’COO’];
User futureUser = new User(firstname = ‘Future’, lastname = ‘User’,
alias = ‘future’, defaultgroupnotificationfrequency = ‘N’,
digestfrequency = ‘N’, email = ‘test@test.org’,
emailencodingkey = ‘UTF-8’, languagelocalekey=’en_US’,
localesidkey=’en_US’, profileid = p.Id,
timezonesidkey = ‘America/Los_Angeles’,
username = ‘futureuser@test.org’,
userpermissionsmarketinguser = false,
userpermissionsofflineuser = false, userroleid = r.Id);
insert(futureUser);
}
}
@isTest
public class UserAndContactTest {
public testmethod static void testUserAndContact() {
InsertFutureUser.insertUser();
Contact currentContact = new Contact(
firstName = String.valueOf(System.currentTimeMillis()),
lastName = ‘Contact’);
insert(currentContact);
}
}
~~~
sObjects That Don’t Support DML Operations
Your organization contains standard objects provided by Salesforce and custom objects that you created. These objects can be accessed in Apex as instances of the sObject data type. You can query these objects and perform DML operations on them. However, some standard objects don’t support DML operations although you can still obtain them in queries. They include the following:
AccountTerritoryAssignmentRule
AccountTerritoryAssignmentRuleItem
ApexComponent
ApexPage
BusinessHours
BusinessProcess
CategoryNode
CurrencyType
DatedConversionRate
NetworkMember (allows update only)
ProcessInstance
Profile
RecordType
SelfServiceUser
StaticResource
Territory2
UserAccountTeamMember
UserPreference
UserTerritory
WebLink
If an Account record has a record type of Person Account, the Name field can’t be modified with DML operations.
Bulk DML Exception Handling
Exceptions that arise from a bulk DML call (including any recursive DML operations in triggers that are fired as a direct result of the call) are handled differently depending on where the original call came from:
1. When errors occur because of a bulk DML call that originates directly from the Apex DML statements, or if the allOrNone parameter of a Database DML method is set to true, the runtime engine follows the “all or nothing” rule: during a single operation, all records must be updated successfully or the entire operation rolls back to the point immediately preceding the DML statement. If the allOrNone parameter of a Database DML method is set to false and a record fails, the remainder of the DML operation can still succeed.
2. You must iterate through the returned results to identify which records succeeded or failed. If the allOrNone parameter of a Database DML method is set to false and a before-trigger assigns an invalid value to a field, the partial set of valid records isn’t inserted.
3. When errors occur because of a bulk DML call that originates from SOAP API with default settings, or if the allOrNone parameter of a Database DML method was specified as false, the runtime engine attempts at least a partial save:
- During the first attempt, the runtime engine processes all records. - Any record that generates an error due to issues such as validation rules or unique index violations is set aside.
- If there were errors during the first attempt, the runtime engine makes a second attempt that includes only those records that didn’t generate errors. All records that didn’t generate an error during the first attempt are processed, and if any record generates an error (perhaps because of race conditions) it’s also set aside.
- If there were additional errors during the second attempt, the runtime engine makes a third and final attempt that includes only those records that didn’t generate errors during the first and second attempts. If any record generates an error, the entire operation fails with the error message, “Too many batch retries in the presence of Apex triggers and partial failures.”
Note
During the second and third attempts, governor limits are reset to their original state before the first attempt.
Apex triggers are fired for the first save attempt, and if errors are encountered for some records and subsequent attempts are made to save the subset of successful records, triggers are refired on this subset of records.
DML: Non-Null Required Fields Values and Null Fields
When inserting new records or updating required fields on existing records, you must supply non-null values for all required fields.
Unlike the SOAP API, Apex allows you to change field values to null without updating the fieldsToNull array on the sObject record. The API requires an update to this array due to the inconsistent handling of null values by many SOAP providers. Because Apex runs solely on the Lightning Platform, this workaround is unnecessary.
DML: String Field Truncation and API Version
Apex classes and triggers saved (compiled) using API version 15.0 and higher produce a runtime error if you assign a String value that is too long for the field.
sObject Properties to Enable DML Operations
To be able to insert, update, delete, or undelete an sObject record, the sObject must have the corresponding property (createable, updateable, deletable, or undeletable respectively) set to true.
DML: ID Values
The insert statement automatically sets the ID value of all new sObject records. Inserting a record that already has an ID—and therefore already exists in your organization’s data—produces an error.
The insert and update statements check each batch of records for duplicate ID values. If there are duplicates, the first five are processed. For the sixth and all additional duplicate IDs, the SaveResult for those entries is marked with an error similar to the following: Maximum number of duplicate updates in one batch (5 allowed). Attempt to update Id more than once in this API call: number_of_attempts.
The ID of an updated sObject record cannot be modified in an update statement, but related record IDs can.
DML: Fields With Unique Constraints
For some sObjects that have fields with unique constraints, inserting duplicate sObject records results in an error. For example, inserting CollaborationGroup sObjects with the same names results in an error because CollaborationGroup records must have unique names.
System Fields Automatically Set
When inserting new records, system fields such as CreatedDate, CreatedById, and SystemModstamp are automatically updated. You cannot explicitly specify these values in your Apex. Similarly, when updating records, system fields such as LastModifiedDate, LastModifiedById, and SystemModstamp are automatically updated.
DML: Maximum Number of Records Processed by DML Statement
You can pass a maximum of 10,000 sObject records to a single insert, update, delete, and undelete method.
Each upsert statement consists of two operations, one for inserting records and one for updating records. Each of these operations is subject to the runtime limits for insert and update, respectively. For example, if you upsert more than 10,000 records and all of them are being updated, you receive an error.
DML: Creating Records for Multiple Object Types
As with the SOAP API, you can create records in Apex for multiple object types, including custom objects, in one DML call with API version 20.0 and later. For example, you can create a contact and an account in one call. You can create records for up to 10 object types in one call.
Records are saved in the same order that they’re entered in the sObject input array. If you’re entering new records that have a parent-child relationship, the parent record must precede the child record in the array. For example, if you’re creating a contact that references an account that’s also being created in the same call, the account must have a smaller index in the array than the contact does. The contact references the account by using an External ID field.
You can’t add a record that references another record of the same object type in the same call. For example, the Contact object has a Reports To field that’s a reference to another contact. You can’t create two contacts in one call if one contact uses the Reports To field to reference a second contact in the input array. You can create a contact that references another contact that has been previously created.
Records for multiple object types are broken into multiple chunks by Salesforce. A chunk is a subset of the input array, and each chunk contains records of one object type. Data is committed on a chunk-by-chunk basis. Any Apex triggers that are related to the records in a chunk are invoked once per chunk. Consider an sObject input array that contains the following set of records:
account1, account2, contact1, contact2, contact3, case1, account3, account4, contact4
Salesforce splits the records into five chunks:
account1, account2
contact1, contact2, contact3
case1
account3, account4
contact4
Each call can process up to 10 chunks. If the sObject array contains more than 10 chunks, you must process the records in more than one call.
Note
For Apex, the chunking of the input array for an insert or update DML operation has two possible causes: the existence of multiple object types or the default chunk size of 200. If chunking in the input array occurs because of both of these reasons, each chunk is counted toward the limit of 10 chunks. If the input array contains only one type of sObject, you won’t hit this limit. However, if the input array contains at least two sObject types and contains a high number of objects that are chunked into groups of 200, you might hit this limit. For example, if you have an array that contains 1,001 consecutive leads followed by 1,001 consecutive contacts, the array will be chunked into 12 groups: Two groups are due to the different sObject types of Lead and Contact, and the remaining are due to the default chunking size of 200 objects. In this case, the insert or update operation returns an error because you reached the limit of 10 chunks in hybrid arrays. The workaround is to call the DML operation for each object type separately.
DML and Knowledge Objects
To execute DML code on knowledge articles (KnowledgeArticleVersion types such as the custom FAQ_kav article type), the running user must have the Knowledge User feature license. Otherwise, calling a class method that contains DML operations on knowledge articles results in errors. If the running user isn’t a system administrator and doesn’t have the Knowledge User feature license, calling any method in the class returns an error even if the called method doesn’t contain DML code for knowledge articles but another method in the class does.
Apex: Locking Statements
In Apex, you can use FOR UPDATE to lock sObject records while they’re being updated in order to prevent race conditions and other thread safety problems.
While an sObject record is locked, no other client or user is allowed to make updates either through code or the Salesforce user interface. The client locking the records can perform logic on the records and make updates with the guarantee that the locked records won’t be changed by another client during the lock period. The lock gets released when the transaction completes.
To lock a set of sObject records in Apex, embed the keywords FOR UPDATE after any inline SOQL statement. For example, the following statement, in addition to 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 Considerations:
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 the records are no longer locked before being able to update the same records. Other clients can still query the same records while they’re locked.
2. If you attempt to lock a record currently locked by another client, your process waits a maximum of 10 seconds for the lock to be released before acquiring a new lock. If the wait time exceeds 10 seconds, a QueryException is thrown. Similarly, if you attempt to update a record currently locked by another client and the lock isn’t released within a maximum of 10 seconds, a DmlException is thrown.
3. If a client attempts to modify a locked record, the update operation can succeed if the lock gets released within a short amount of time after the update call was made. In this case, it’s possible that the updates overwrite changes made by the locking client if the second client obtained an old copy of the record. To prevent the overwrite 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. The second client can use this copy to make new updates.
4. The record locks that are obtained in Apex via FOR UPDATE clause are automatically released when making callouts. The information is logged in the debug log and the logged message includes the most recently locked entity type. For example: FOR_UPDATE_LOCKS_RELEASE FOR UPDATE locks released due to a callout. The most recent lock was Account. Use caution while making callouts in contexts where FOR UPDATE queries could have been previously executed.
5. When you perform a DML operation on one record, related records are locked in addition to the record in question.
Locking in a SOQL For Loop
The FOR UPDATE keywords can also be used within SOQL for loops. For example:
for (Account[] accts : [SELECT Id FROM Account
FOR UPDATE]) {
// Your code
}
As discussed in SOQL For Loops, the example above corresponds internally to calls to the query() and queryMore() methods in the SOAP API.
Note that there is no commit statement. If your Apex trigger completes successfully, any database changes are automatically committed. If your Apex trigger does not complete successfully, any changes made to the database are rolled back.