Salesforce Platform Cache & Partitions using Apex

Platform Cache

Platform Cache is a memory layer that stores Salesforce session and org data for later access which improves the performance of your applications. Platform Cache improves performance by distributing cache space so that some applications or operations don’t steal capacity from others.
Salesforce allow you to create partitions. Partitions allow you to distribute cache space in the way that works best for your applications. Caching data to designated partitions ensures that it’s not overwritten by other applications or less-critical data.

When you use Platform Cache, your applications will run faster because they store reusable data in memory. Applications can quickly access this data; they don’t need to duplicate calculations and issue requests to the database on subsequent transactions. In short, think of Platform Cache as RAM for your cloud application.

 

Let’s enable the Platform Cache and do some hands-on !!

Turn On Platform Cache

1. Go to Platform Cache.

Platform Cache.png
2. Request Trial Capacity.
Trail Capacity
3. Create a New Platform Cache Partition.
Screen Shot 2018-10-09 at 2.47.27 PM.png
3.  To use Platform Cache, first set up is to create at least one partition. Once you’ve set up partitions, you can add, access, and remove data from them using the Platform Cache Apex API.
Screen Shot 2018-10-15 at 3.20.28 PM
We have turned on the Platform Cache and created one partition called “DefaultCache”.

Org & Session Cache

There are two types of cache:
  • Org cache—Stores data that any user in an org reuses. For example, the contents of navigation bars that dynamically display menu items based on user profile are reused.

    Unlike session cache, org cache is accessible across sessions, requests, and org users and profiles. Org cache expires when its specified time-to-live (ttlsecs value) is reached.

  • Session cache—Stores data for individual user sessions. For example, in an app that finds customers within specified territories, the calculations that run while users browse different locations on a map are reused.

    Session cache lives alongside a user session. The maximum life of a session is eight hours. Session cache expires when its specified time-to-live (ttlsecs value) is reached or when the session expires after eight hours, whichever comes first.

Scenario:  A Developer wants to store the list of Contacts for given Account in Cache. We can implement this scenario using Org & Session Cache using Default Partition or a specific Partition.
Here are the implementation scenarios for both type of cache:

1) Implement Org Cache using Apex

The Org cache uses two classes Cache.Org and Cache.OrgPartition to the access values stored in cache.
Using Default Partition:
In the below code, the default partition named “DefaultCache” will be used to store the contacts in cache. We can make default cache by checking the box called “Default Partition” while creating cache as given in the above steps.

Screen Shot 2018-10-15 at 4.13.14 PM

If there a Apex Class:
public class ContactCacheDefaultPartion {
    
    public static void putContacts(String accId, Contact[] contacts) {
        Cache.org.put(accId,contacts);
    }
    
    public static Contact[] getContacts(String accId) {
        List conList = (List)Cache.Org.Get(String.valueOf(accId));
        return conList;
    }
    
}
Every time you call the put operation the value in the cache will be overridden if there a change to the values for a given key.
The parameter ‘true’ when specified will prevent overriding cached value when calling from another Namespace/package.
The following example also sets the lifetime of the cached value (3600 seconds or 1 hour) and makes the value available to any namespace.
Cache.Org.put(accId, contacts, 3600, Cache.Visibility.ALL, true);

With Specific Partition:
We can create a new partition called “ContactCache” without checking the box Default Cache. This will store the contacts in allocated partition given by partition name ‘ContactCache”. Minimum Partition size can be 5 MB.
Apex Class:
public class ContactCachePartion {

    public static void putContacts(String accId, Contact[] contacts) {
        Cache.OrgPartition orgPart = 
                     Cache.Org.getPartition('local.ContactCache');
        orgPart.put(accId, contacts);
    }
    
    public static Contact[] getContacts(String accId) {
        Cache.OrgPartition orgPart = 
                      Cache.Org.getPartition('local.ContactCache');
        
        List conList = (List)orgPart.get(String.valueOf(accId));
        return conList;
    }
}

Run the script in Developer Console:

In the below code, we are calling the methods defined in the apex class. It stores the contacts in cache and displaying the data in debug logs. The main feature of Cache is to get the same data which is already stored in memory even running the script multiple times.

//e.g. Using Cache Default Partition
List accList = [Select Id,(Select Id,Name From Contacts) 
From Account Limit 2]; 

ContactCacheDefaultPartion.putContacts(accList[0].Id,accList[0].Contacts); 
ContactCacheDefaultPartion.putContacts(accList[1].Id,accList[1].Contacts); 
System.debug('1st Account: '+ ContactCacheDefaultPartion.getContacts(accList[0].Id)); 
System.debug('2nd Account: '+ ContactCacheDefaultPartion.getContacts(accList[1].Id)); 


//e.g. Using Specific Cache Partition
List accList = [Select Id,(Select Id,Name From Contacts) 
                       From Account Limit 2];
ContactCachePartion.putContacts(accList[0].Id,accList[0].Contacts);
ContactCachePartion.putContacts(accList[1].Id,accList[1].Contacts);

System.debug('1st Account: '+ ContactCachePartion.getContacts(accList[0].Id));
System.debug('2nd Account: '+ ContactCachePartion.getContacts(accList[1].Id));


Output

Screen Shot 2018-10-16 at 9.46.31 PM.png

2) Implement Session Cache using Apex

The session cache is similar to the org cache, except the class names are different. A developer can use the Cache.Session and Cache.SessionPartition classes to access values stored in the session cache.

Please re-use the Org cache code above implemented with the following changes to implement Session cache:

Using Default Partition:
Cache.Session.put(accId, contacts, 3600, Cache.Visibility.ALL, true);
With Specific Partition:
// Get partition
Cache.SessionPartition sessionPart = 
          Cache.Session.getPartition('local.ContactCache');

// Add cache value to the partition
sessionPart.put(accId, contacts);
// Retrieve cache value from the partition
List conList = (List)sessionPart.Get(String.valueOf(accId));

 

Using Default Partition Vs. Using Specific Partition

The code with default partition automatically access the default cache. There can be only one default cache at a time. When using specific partition, we have to always mention the name of partition in apex code.

Reference:

https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_namespace_cache.htm

https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_platform_cache_session_examples.htm

Author:

Ajomon Joseph AJOMON JOSEPH
Senior Salesforce Architect
In Connect me on LinkedIn
twitter Follow me on Twitter @apexcoder
blogger Subscribe to my blog https://apexcoder.com
email Email me ajomon@apexcoder.com

Lightning Locker Service

Lightning Locker is a layer which sits in between your browser and DOM (document object). In other words, Lightning Locker is a virtual browser that allows only secure request to go through from client to server. This virtual browser sits in front of your real browser.  It creates a separate layer. Without such a layer it is unsafe as any one can get access to complete DOM and can easily manipulate it.

Lightning locker service is automatically enabled in all Salesforce orgs since summer 17 with the API version 40.0 and above.

Why there is a need for Lightning Locker? 

In JavaScript world there is a single Window object, representing the browser’s window, which all global objects, functions and variables are attached to.

In Lightning Components world, with the locker service enabled, the single Window object changes somewhat. Instead of a Window, your components see )a SecureWindow object which is shared among all components in the same namespace. This SecureWindow is isolated from the real Window for security reasons. In practice, this means that if you mix locker and non-locker Lightning Components on the same page, there are two different window concepts which know nothing about each other.

Example:

The example here is a lightning application that attaches a String to the Window object when it initialize and includes two components that each attempt to access this variable from the window, one at ApI 40 and one at Api 39. The app also attempts to access the variable, just to show that it is correctly attached.

Application – LockerServiceDemo.App 

Screen Shot 2018-10-02 at 9.36.29 PM.png

The above app is in version 40 and above, and it has has two components one locker and the other non locker. There is a button on the application, and one button each on locker and the other non locker component. When the application is loaded we are setting the value for window.testValue. The window is namespace and testValue is a string variable attached to the window. The goal is to click on all three buttons and display the variable value from window.testValue.

Application JS Controller – LockerServiceDemo.js

Screen Shot 2018-10-02 at 9.37.23 PM

Please see below the locker and the other non locker components and related JS Controllers:

LockerWindow.cmp  (use version 40 and above through bundle version settings)

Screen Shot 2018-10-02 at 9.39.43 PM.png

LockerWindow.js

Screen Shot 2018-10-02 at 9.48.00 PM.png

NonLockerWindow.cmp (use version 39 through bundle version settings)

Screen Shot 2018-10-02 at 9.41.52 PM.pngNonLockerWindow.js

Screen Shot 2018-10-02 at 9.48.00 PM.png

Output:

Let’s click each of the three buttons and see the alerts below:

App:

Screen Shot 2018-10-03 at 9.28.07 PM

Non Locker Component

Screen Shot 2018-10-03 at 7.48.49 PM

Locker Service Component:

Screen Shot 2018-10-03 at 9.28.07 PM

As the application is running with the locker service enabled, the variable is attached to a secure window. The locker component is also attached to same secure window. Hence in both these cases window.testValue gets the value “From the Window” as displayed above.

The non-locker service component cannot access the secure window, hence the variable is “undefined”.

 
References

https://developer.salesforce.com/blogs/developer-relations/2016/04/introducing-lockerservice-lightning-components.html

Author:

Ajomon Joseph AJOMON JOSEPH
Senior Salesforce Architect
In Connect me on LinkedIn
twitter Follow me on Twitter @apexcoder
blogger Subscribe to my blog https://apexcoder.com
email Email me ajomon@apexcoder.com

 

 

Lightning Series 3 – Building a Simple Lightning App to View, Edit and Delete Records

In our previous series, we have talked about searching and inserting new records using lightning components. In this blog, we gonna talk about how we can view, edit and delete the records in lightning experience.

VIEW, EDIT AND DELETE RECORD FROM THE LIST OF RECORDS DISPLAYED IN TABLE

StaffView

We need to create below three elements to build the complete functionality:

  1. Apex Class
  2. Lightning Component
  3. JS controller

Apex Class:

Below apex class has different methods invoked based on the action required from user like viewing, updating and deleting the records.  @AuraEnabled enables you to access this method in lightning component.

/************************************************************

Name: CreatestaffRecord
Type: Apex Class  
Purpose: Apex Class  for  lightning component 
CreatestaffRecordController.cmp
***********************************************************/
public with sharing class CreateStaffRecord {
    
    @AuraEnabled
    public static void updateRecord(Staff__c staff){
        try{
            update staff;
        }catch(DMLException e){
            throw new AuraHandledException(e.getDMLMessage(0)); 
        }catch(Exception e){
            throw new AuraHandledException(e.getMessage()); 
        }       
    }  
    
    @AuraEnabled
    public static Staff__c deleteRecord(Staff__c staff){
        try{
            if(staff != null){
                delete staff;
            }
        }catch(DMLException e){
            throw new AuraHandledException(e.getDMLMessage(0)); 
        }catch(Exception e){
            throw new AuraHandledException(e.getMessage()); 
        }  
        return staff;
    }
}

Lightning Component:

Let’s hit on the ground and create a component to view, edit and delete a record of  Staff.

Please click here to view the code:

JS Controller:

StaffRecordViewController.js

({
    doInit : function(component){
        
        // Get a reference to the getWeather() function defined in the Apex controller
        var action = component.get("c.getStaffRecord");
        action.setParams({
            "staffId": component.get("v.recordId")
        });
        // Register the callback function
        action.setCallback(this, function(response){
            // Set the component attributes using values returned by the API call
            if (response.getState() == 'SUCCESS') {
                var staff=response.getReturnValue();
                
                component.set("v.staff", staff);
                component.set("v.isEditMode", false);
            }
        });
        // Invoke the service
        $A.enqueueAction(action);
    },
    
    handleStaffEvent : function(component, event) {
        var recId = event.getParam("recordId");
        // set the handler attributes based on event data
        component.set("v.recordId", recId);

        var action = component.get('c.getStaffRecord');
        
        action.setParams({"staffId": recId});
        
        action.setCallback(this, function(response){
            
            if (response.getState() == 'SUCCESS') {
                
                
                var staff=response.getReturnValue()
                component.set("v.staff", staff);
                
                component.set("v.isEditMode", false);
            }
        });
        $A.enqueueAction(action);   
       
    },
    editStaff : function(component){        
        component.set("v.isEditMode",true);
    },
    cancelPage : function(component){        
        component.set("v.isEditMode",false);
    },    
    saveStaff : function(component, event, helper){
        // Get a reference to the updateRecord() function defined in the Apex controller
        //getting the staff information
        var staff = component.get("v.staff");       
        
        //Make field required from front end (Not from DB)
        if(staff.Last_Name__c==''){
            alert('Missing Last name');
            return false;
        }
        
        
        var action = component.get("c.updateRecord");
        action.setParams({
            staff : staff
        });
        // Register the callback function
        action.setCallback(this, function(response){
            var state = response.getState();
            
            // Set the component attributes using values returned by the API call
            if (state == "SUCCESS") {
                    
                component.set("v.staff", staff);
                
                component.set("v.isEditMode", false);
               
            }else if(state == "ERROR"){
                //errors coming from DB thrown by controler catch blocl
                let errors = response.getError();
                let message;
                // Retrieve the error message sent by the server
                if (errors && Array.isArray(errors) && errors.length > 0) {
                    message = errors[0].message;
                }
                
                alert(message);
            }
        });
        // Invoke the service
        $A.enqueueAction(action);
    },
    
    delStaff : function(component, event, helper){
        // Get a reference to the updateRecord() function defined in the Apex controller
        //getting the staff information
        var staff = component.get("v.staff");        
        var action = component.get("c.deleteRecord");
        action.setParams({
            staff : staff
        });
        
        // Register the callback function
        action.setCallback(this, function(response){
            // Set the component attributes using values returned by the API call
            if (response.getState() == 'SUCCESS') {
                component.set("v.staff", staff);
                component.set("v.isEditMode", false);
            }
        });
        
        
        // Invoke the service
        $A.enqueueAction(action);
        alert("Record is deleted.");
        
        var urlEvent = $A.get("e.force:navigateToURL");
        urlEvent.setParams({
            "url": "/a00/o"
        });
        
        urlEvent.fire();
    }
})     
    

Author:

Ajomon Joseph AJOMON JOSEPH
Senior Salesforce Architect
In Connect me on LinkedIn
twitter Follow me on Twitter @apexcoder
blogger Subscribe to my blog https://apexcoder.com
email Email me ajomon@apexcoder.com

Lightning Series 2 – Building Simple Lightning App to insert a record

Salesforce Lightning is a component-based framework for application development. A Developer can build responsive applications for any device using lightning which include an out-of-the-box set of components, event-driven architecture, and a framework optimized for performance.

The Lightning Component framework is a UI framework for developing dynamic web apps for mobile and desktop devices. It uses JavaScript on the client side and Apex on the server side.

Let’s find out how we can build an interface to insert record to database on Lighting.

INSERT RECORD USING LIGHTNING COMPONENT

createstafflightning

We need to create below three elements to build the complete functionality:

  1. Apex Class
  2. Lightning Component
  3. JS controller

Apex Class:

In the above code, we have used @AuraEnabled annotation which enables client- and server-side access to an Apex controller method. Providing this annotation makes your methods available to your Lightning components.

 public with sharing class CreateStaffRecord {
    
/**
* Create a new staff Record
* @param Staff__c staff  staff record to be inserted
*/
    @AuraEnabled
    public static Staff__c createRecord(Staff__c staff){
        try{
            insert staff;
            
        }catch(DMLException e){
            throw new AuraHandledException(e.getDMLMessage(0)); 
        }catch(Exception e){
            throw new AuraHandledException(e.getMessage()); 
        }
        return staff;
    }

Lightning Component:

Let’s hit on the ground and create a component to insert a new record for  Staff.  There are some steps we need to follow before creating a lightning component. The Developer Console is a convenient, built-in tool we can use to create new and edit existing Lightning components and other bundles.

  1. Enable My domain.
  2. Open the Developer Console.
    Select Developer Console from the Your Name or the quick access menu (Setup gear icon).
    1. Open the New Lightning Bundle panel for a Lightning component.
      Select File | New | Lightning Component.
    2. Name the component CreateStaffRecord.
    3. Click Submit to create the component
    4. Inside the code window, use the below code and we will discuss about this code in detail:

Please click here to view the code:

In the above code, we have used below tags and the explanation is written for each of these tags:

Links:

({

create : function(component, event, helper) {
//getting the staff information
var staff = component.get(“v.staff”);

//Make field required from front end (Not from DB)
if(staff.Last_Name__c==”){
alert(‘Missing Last name’);
return false;
}

//Calling the Apex Function
var action = component.get(“c.createRecord”);

//Setting the Apex Parameter
action.setParams({
staff : staff
});

//Setting the Callback
action.setCallback(this,function(response){
//get the response state
var state = response.getState();
//check if result is successfull
if(state == “SUCCESS”){
//Reset Form
var newstaff = {‘sobjectType’: ‘staff__c’,’First_Name__c’: ”,
‘Last_Name__c’: ”,’Email__c’: ”,’Phone__c’: ”,’Start_Date__c’:”
};
//Resetting the Values in the form
component.set(“v.staff”,newstaff);

alert(‘Record is Created Sucessfully’);

var urlEvent = $A.get(“e.force:navigateToURL”);
urlEvent.setParams({
“url”:”/lightning/o/Staff__c/list?filterName=Recent&0.source=alohaHeader”
});
console.log(urlEvent);
urlEvent.fire();

} else if(state == “ERROR”){
//errors coming from DB thrown by controler catch blocl
let errors = response.getError();
let message;
// Retrieve the error message sent by the server
if (errors && Array.isArray(errors) && errors.length > 0) {
message = errors[0].message;
}

alert(message);
}
});
//adds the server-side action to the queue
$A.enqueueAction(action);

}
})


Author:

Ajomon Joseph AJOMON JOSEPH
Senior Salesforce Architect
In Connect me on LinkedIn
twitter Follow me on Twitter @apexcoder
blogger Subscribe to my blog https://apexcoder.com
email Email me ajomon@apexcoder.com

 

Lightning Series 1 – Building a Simple Lightning App to Search and list Records

Let’s start searching and listing records through lightning components. We used to create tables using pageBlockTable in visualforce pages. In lightning, we either use HTML tables or inbuilt component tags like (lightning:datatable) to display the data in rows and columns format. Data tables are an enhanced version of an HTML table and are used to display tabular data. Here in this example, we use html table to display the data which enhance the user interface and speed up the searching and retrieve the relevant data. We are using SLDS library with HTML tags to support the CSS styles provided by Salesforce.

SLDS Library

https://www.lightningdesignsystem.com/components/overview/

Data Table

https://www.lightningdesignsystem.com/components/data-tables/

SEARCH AND DISPLAY RECORDS USING LIGHTNING COMPONENT

searchPage.PNG

Here is the order of execution of a lightning component:

Lightning Component invokes JSController, which further invokes the APEX Class. The Lightning Component can’t directly communicate with the APEX Class. It has to be always invoked via JSController.

How Lightning Component communicates with controllers or vice versa ?

The syntax “v.” is used to get and set the value of the Lightning Component variable from JSController.

“c.” is used to invoke controller methods or access variables. If “c.” is used on the Lightning Component, this means we are accessing JSContoller variables/methods. If “c.” is used in the JSController, then we are accessing APEX Class variables/methods.

Building Search and List Component

Let’s build a search and list Lighting Component. We need to create below six items to build this search functionality:

  1. Apex Class
  2. Lightning Component
  3. JS Controller
  4. Event
  5. Lighting Tab
  6. Lightning App

Apex Class

Below apex class returns the list of staff based on the name entered on the search box displayed on the component. @AuraEnabled enables you to access this method in lightning component.

searchStaffController.apxc

public class searchStaffController {
    @AuraEnabled
    public static List  fetchStaff(String searchKeyWord) {
        String searchKey = searchKeyWord + '%';
        List  returnList = new List  ();
        List  lstOfStaff = [select Id, Name,First_Name__c, Middle_Name__c, Last_Name__c,Phone__c,Email__c,Status__c from Staff__c where First_Name__c LIKE: searchKey OR Last_Name__c LIKE: searchKey];
        
        System.debug('Staff **** ' + lstOfStaff);
        
        
        for (Staff__c staff: lstOfStaff) {
            returnList.add(staff);
        }
        return returnList;
    }
}

Lightning Component:

Lightning Component acts like a visualforce page which displays all the components required to get inputs from user and display the output. This component contains search box, buttons , and data tables to display the relevant records.

There are some steps we need to follow before creating a lightning component. The Developer Console is a convenient, built-in tool we can use to create new and edit existing Lightning components and other bundles.

  1. Enable My domain.
  2. Open the Developer Console.
    Select Developer Console from the Your Name or the quick access menu (Setup gear icon).
    1. Open the New Lightning Bundle panel for a Lightning component.
      Select File | New | Lightning Component.
    2. Name the component SearchStaff.
    3. Click Submit to create the component
    4. Inside the code window, use the below code and we will discuss about this code in detail:

Please click here to view the lightning component code: searchStaff.cmp

JS Controller:

This controller is client based java script controller which invokes the actions driven by component and further invokes apex class ‘s method to search staff records. In the below code, we have three parameters like component, event, helper which stores the information of component variables, event and associated apex class method info.

This is a client-side controller handles events within a component. It’s a JavaScript resource that defines the functions for all of the component’s actions.
Please follow the steps below to create JS controller:
1) In the developer console, we need to open the staff component.
2) we can simply press Ctrl + Shift + 2 or double click on the “CONTROLLLER” on the right pallate.
rightPalllate.PNG
In the below code, we have created a function “Search” which search the staff record:
searchStaffController.js
({
Search: function(component, event, helper) {

component.set("v.isNew",false);
var searchKeyFld = component.find("searchId");
var srcValue = searchKeyFld.get("v.value");
if (srcValue == '' || srcValue == null) {
// display error message if input value is blank or null
searchKeyFld.set("v.errors", [{
message: "Enter Search Keyword."
}]);
} else {
searchKeyFld.set("v.errors", null);
// call helper method
helper.SearchHelper(component, event);
}
},
navigateToStaff : function(component, event, helper) { 

component.set("v.isNew",false);
component.set("v.isView",true);
component.set("v.objId",event.target.id); 
component.set("v.headerText","My Staff");

// Get the application event by using the
// e.. syntax
var appEvent = $A.get("e.c:StaffViewCompEvent");
appEvent.setParams({"recordId" : event.target.id});
appEvent.fire();

} ,
createNew : function(component){ 
component.set("v.headerText","New Staff");
component.set("v.isNew",true);
component.set("v.isView",false);

},
searchShow : function(component){ 
component.set("v.headerText","Search Staff");
component.set("v.isNew",false);
component.set("v.isView",false);

}

})
searchStaffHelper.js
({
 SearchHelper: function(component, event) {
 var action = component.get("c.fetchStaff");
 action.setParams({
 'searchKeyWord': component.get("v.searchKeyword")
 });
 action.setCallback(this, function(response) {
 var state = response.getState();
 if (state === "SUCCESS") {
 var storeResponse = response.getReturnValue();
 // if storeResponse size is 0 ,display no record found message on screen.
 if (storeResponse.length == 0) {
 component.set("v.infoMessage", true);
 } else {
 component.set("v.infoMessage", false);
 }
 // set numberOfRecord attribute value with length of return value from server
 component.set("v.numberOfRecord", storeResponse.length);
 // set searchResult list with return value from server.
 component.set("v.searchResult", storeResponse);
 }
 
 });
 $A.enqueueAction(action);
 
 },
})

 

Event: 

In the Lightning framework, events are fired from JavaScript controller actions. Events can contain attributes that can be set before the event is fired and read when the event is handled. This event named “StaffViewCompEvent” helps to navigate to the staff page when clicked on any staff in the results.

staffViewCompEvent.evt

 

 


Link: https://developer.salesforce.com/docs/atlas.en-us.lightning.meta/lightning/events_component_create.htm

Lightning Tab

Tab helps to display the component to create a record using component. In order to create a lightning tab. Please follow the below instruction:

The component must include below markup:- (we have already done this)

Here is the link to create lightning tab step by step:

https://developer.salesforce.com/docs/atlas.en-us.lightning.meta/lightning/aura_add_cmp_lex.htm

NOTE: We need to choose the lightning component based on the name which we have created in this blog.

Lightning App

Let’s first create an Lightning app to store the tabs which we will use to plug lighting component:

In the Lightning experience, click on setup > Apps > App Manager > Click on New Lighting App. On the page “Select Items” , we need to select our created lightning component created in this blog.

In this blog, we have described the lighting components and code to search and display the data. The component code also contains the reference of other components which we will discuss in our next series like viewing and editing the records etc.

To be continued…

Author:

Ajomon Joseph AJOMON JOSEPH
Senior Salesforce Architect
In Connect me on LinkedIn
twitter Follow me on Twitter @apexcoder
blogger Subscribe to my blog https://apexcoder.com
email Email me ajomon@apexcoder.com

  

How to deploy apex code using ANT ?

A developer want to push the components/metadata like Objects, Fields , Validation Rules, Workflows and Apex Code etc. We can use Change Set, Force.com IDE and ANT Migration tool. However there are some extra features provided in this ANT migration tool. In this blog, we are gonna demonstrate the most powerful tool ANT mostly used for our major deployments.

ANT migration tool (based on JAVA), is used to deploy the Metadata from one organization to other organization or we can use it to retrieve the metadata from one organization and then make some changes locally and then deploy that metadata again to the same organization.

Features of ANT Migration Tool

  • The main advantage of this tool is, that it gets the metadata in form of XML files from your server and downloads it locally on your computer. Thus you can make changes in those XML files locally and again deploy the changes to any server instance, any target org that you want.
  • It allows you to deploy the same metadata any number of times to any of your server, as you have downloaded the metadata in form of XML files, you can deploy them again and again.
    • Change set does not allow you to delete any metadata component from target org. But using ANT migration tool you can delete the components from target org. This is done using destructiveChanges.xml file.
  • Some components are not supported to be migrated using change sets but you can migrate them using ANT migration tool.
  • It can also be run from command prompt using some specific commands for calling APIs.
  • You can also automate your migration process leveraging the capabilities of command prompt .bat files and XML structure of source files.

Here are the few steps that we need to take in order to push our changes:-

Step 1 (Install ANT Migration Tool)

Please click here to install prerequisites and ANT Migration Tool. Save the .zip file locally, and extract the contents to the directory of your choice. If you are using Windows, please use a folder in C:\ for example ‘C:\Tools\apache-ant-1.10.3’ to avoid windows limitation for directory length.

In order to start deployment, we need to download the .zip file which contains set of configuration files. This helps developers to configure username and password, getting metadata and build scripts to run on command prompt. 

( The above download link doesn’t require authentication to Salesforce. If you’re logged in to Salesforce, we recommend you log out before accessing the link in your browser. )

Please extract the salesforce_ant_42.0.zip to ‘C:\Tools\salesforce_ant_41.0’. Will discuss about the each file later in this blog. Here is the screenshot of sample files.

ant

Step 2 (Setup Environment Variables)

You need to set the environment path variables which will help you to access ant tools from any folder through the command line.

Go to Control Panel > System > Edit the System Environment Variables

env

  • Create a ANT_HOME and JAVA_HOME variables under ‘System Variables’
  • Create an ANT_HOME environment variable and set the value to where you have installed Ant. e.g. C:\Tools\apache-ant-1.10.3
  • Create JAVA_HOME environment variable and set the value to the location of your JDK. e.g. C:\Program Files\Java\jdk1.8.0_171
  • Add C:\Tools\apache-ant-1.10.3\bin to the list of existing PATH variablepath

Step 3 (Test Installation)

Once you done with the above steps now it’s time to check if everything is all set to go.

  • Open command prompt or terminal and type ant or ant –version and press enter

cmd

To get started  first, let’s understand the important files available inside the salesforce -ant folder (C:\Tools\salesforce_ant_41.0\sample):

build.properties

We provide the login credentials in build.properties. Open build.properties and enter your Dev Org credentials. Don’t forget to append the security token to the password.

build

build.xml

Here we provide the scripts to retrive or deploy or any other possible operation. We use tags like “retrieveCode” or “deployCode” in build.xml. These tags are also called targets. We can run these targets one by one and complete the task in sequential manner as we want. build.properties is also referred in build.xml for required credential and login urls.

  • Search for “retrieveCode” in build.xml. You will see unpackaged=”codepkg/package.xml”

retrieveCode

  • Open package.xml from ‘codepkg/package.xml’, and modify package.xml. You will enter the class and trigger you like to retrieve from Salesforce in package.xml

package

This is the last xml file that we need. We have to mention the API name of components that we want to retrieve from source org. And the same xml can be used while deploying those components to target org.

Step 4 (Retrieve and Deploy Code)

deployCode
You may navigate to Salesforce > Deployment Status to verify deployment
deploymnet
Deployment without running Test Class
In this case, we are not running the tests which can be configured in build.xml by setting testLevel=”NoTestRun”
notestrun
Deployment with running Specified Tests
We can run specific test class by setting testLevel=”RunSpecifiedTests” with adding below runTest child tag in the above script.
TestClass1
TestClass2
runspecifictests

References

 

For more details about build.xml scripts, please click on following link

How to delete a class or trigger, please click on the following link

To rename a component, you must delete the component, and then recreate it with a new name. It cannot be done directly because we don’t maintain the ID’s of components in the case of ANT. Please click on the following link for more information

Author:

Ajomon Joseph AJOMON JOSEPH
Senior Salesforce Architect
In Connect me on LinkedIn
twitter Follow me on Twitter @apexcoder
blogger Subscribe to my blog https://apexcoder.com
email Email me ajomon@apexcoder.com

 

SOQL Query Analysis using Query Plan tool and Optimization using filters & Custom Indexes.

Why SOQL Optimization is Needed ?

SOQL is a tool that lets you access records in your Salesforce database. When you don’t write good SOQL queries you’re going to hit the governor limit of non selective query. There is a very common issue that most of us have came across when there are more than 100k records in a given table. For e.g.  If we cross the threshold of 100k records, we might start to see some “non-selective query” type errors mentioned below:

Error Non Selective

 

SOQL query should always be selective meaning it must contain at least one query filter on an indexed field and that filter in turn reduces the resulting number of rows below a Salesforce-determined threshold*.

Analyze SOQL queries using Query Plan tool 

When it comes to optimize the SOQL, we first need to analyze the SOQL to identfiy the weak areas of SOQL and it’s running performance. The Query Plan tool shows the cost of Salesforce executing a given SOQL query given the database statistics known at that time.

Enabling Query Plan

Salesforce doesn’t have the Query Plan feature enabled by default. To enable it, open the Developer Console –> Help –> Preferences and then check “Enable Query Plan”.

Using Query Plan

After the Query Plan is enabled, open the Query Editor tab at the bottom, enter a SOQL query, and click the Query Plan button. This will open a modal showing the cost of the SOQL query along with helpful tips.

QueryPlan

  • Cardinality – The estimated number of records that will be returned by the query.
  • Fields – The indexed field(s) used by the Query Optimizer. If the leading operation type is Index, the fields value is Index. Otherwise, the fields value is null.
  • Leading Operation Type – The primary operation type that Salesforce will use to optimize the query.
    • Index – The query will use an index on the queried object.
    • Sharing – The query will use an index based on the sharing rules associated with the user who is executing the query. If there are sharing rules that limit which records that user can access, Salesforce can use those rules to optimize the query.
    • TableScan – The query will scan all records for the queried object.
    • Other – The query will use optimizations internal to Salesforce.
  • Cost – The cost of the query compared to the Force.com Query Optimizer’s selectivity threshold. Values above 1 mean that the query won’t be selective.

In above example, we query all Accounts with non-selective filter which is “Is Deleted”, the cost is high.

  • SObject Cardinality – The approximate record count for the queried object.
Salesforce Query Plan How To
Optimize SOQL using Filters and Custom Index : 
  • We must only select the fields which are needed but not all fields.
  • We should try to use the limit operator to reduce how many records are returned.
  • We must use indexed fields- selective filters

Here are the three examples which shows how the initial non-selective query is being optimized with adding more filters to it.

// Non- Selective Query- This will return everything from the table 

List = [SELECT Id, Name, Email, OwnerId from Contact ];

// Better Query than above one, we’ve got a filter but it is not on an indexed field

List = [SELECT Id, Name, Email, OwnerId from Contact Where Status = ‘Active’];

// Even better since Owner is one of the standard indexed fields. The performance of the SOQL //query improves when two or more filters used in the WHERE clause meet the mentioned //conditions.

List = [SELECT Id, Name, Email, OwnerId from Contact Where Status = ‘Active’ and OwnerId IN (‘005d0000001acWo’,’005d0000001mnTo’) ];

Developer must use Indexed fields to improve the performance of SOQL query.

Custom Index and Considerations 

Salesforce leverages indexes heavily to optimize query performance whenever possible. An index is a separate data structure that stores field values in such a way that it’s fast to search on them to identify the rows we’d like returned. The general concept is the same as a book’s index.

What fields are pre- indexed?

  • Object Ids
  • Object Name – Auto Number or Text Field.
  • Custom Relationsips – Master Details and Lookups.
  • External Ids
  • Unique Fields
  • Owner Field

Use an index in the where clause whenever possible. However, this doesn’t guarantee that it will be used. Salesforce has various rules around when an index will be used or not based on its selectivity.

  • An indexed field will not be used under the following conditions:
    • Not In (…)

e.g. Status__c Not IN (‘Active’) – Even if Status__c is indexed field, it won’t be considered as indexed field and might occur non-selective error. 

    • Like with wildcards “%Field%”
    • !=
    • Excludes for picklist values
    • Not Like
    • Comparison Operators Paired With Text Fields. Text_Field ,

Not sure if a field is indexed or not ? Open an object’s definition page and notice that there’s an “Indexed” column. If it’s checked, it’s indexed.

Indexed

How to get field Indexed which is not indexed ?

Please submit a case with Salesforce Support.

For more information custom index:-

 
Author:

Ajomon Joseph AJOMON JOSEPH
Senior Salesforce Architect
In Connect me on LinkedIn
twitter Follow me on Twitter @apexcoder
blogger Subscribe to my blog https://apexcoder.com
email Email me ajomon@apexcoder.com