Monday, May 8, 2023

Difference Between Custom Settings and Custom Metadata

Here is list of Difference Between Custom Settings and Custom Metadata Types

  1. Custom settings enable you to create custom sets of data, as well as create and associate custom data for an organization, profile, or specific user. All custom settings data is exposed in the application cache, which enables efficient access without the cost of repeated queries to the database. Custom metadata are like custom setting but records in custom metadata type considered as metadata rather than data. These are typically used to define application configurations that need to be migrated from one environment to another, or packaged and installed.
  2. There are 2 types of custom setting List and Hierarchy Custom setting. There are no such types in custom metadata. Custom metadata does not support Hierarchy type of data based on user profile or specific user.
  3. You can control the visibility of custom setting by specifying it as public or protected. If custom setting is marked as protected, the subscriber organization will not be able to access the custom setting. If it is marked as public, then subscriber org can also access it. You can control the visibility of Custom Metadata Types by specifying it as public or protected. If it is marked as public type, then anyone can see it. If it is marked as protected type, in the installed managed package subscriber organization, only Apex code in that managed package can use it.
  4. Custom settings do not support relationship fields. You can create lookups between Custom Metadata objects.
  5. You can access custom setting data using instance methods and can avoid SOQL queries to database. With custom metadata types, you can issue unlimited Salesforce Object Query Language (SOQL) queries for each Apex transaction.
  6. Custom metadata type are visible in test class without “SeeAllData” annotation but custom setting data is not visible.
  7. Custom metadata records are deployable and packageable. But we can not deploy custom setting data.

Monday, May 1, 2023

Mixed DML Operation Error in Salesforce

A mixed DML Operation Error in Salesforce comes when you try to perform DML operations on setup and non-setup objects in a single transaction. DML operations on certain sObjects, sometimes referred to as setup objects(User), can’t be mixed with DML on other sObjects(Standard/Custom Objects) in the same transaction.

How do you resolve a mixed DML exception?

  • To avoid this error, we should perform DML operation on standard/custom object records in a different transaction.
  • In general all the apex classes and apex triggers execute synchronously (execute immediately).
  • If we perform DML operation on standard/custom object records asynchronously (execute in future context), we can avoid MIXED-DML-OPERATION error.
  • To execute logic asynchronously keep the logic in an apex method (in a separate apex class, not in same apex trigger) which is decorated with @future annotation.

In the below example, Account and User records are inserted in the
same transaction.
This is will throw the Mixed DML Exception.

public class MixedDMLError {
  public static void myMethod() {
      Account a = new Account(Name='XploreSFDC');
      insert a;
      Profile p=[SELECT Id FROM Profile WHERE 
Name='Standard User'];
      UserRole r = [SELECT Id FROM UserRole WHERE Name='CEO'];
      User usr = new User(alias = 'XploreSFDC'
          email='XploreSFDC@XploreSFDC.com'
          emailencodingkey='UTF-8', lastname='Max'
          languagelocalekey='en_US'
          localesidkey='en_US', profileid = p.Id
userroleid = r.Id,
          timezonesidkey='America/Los_Angeles'
          username='XploreSFDC@XploreSFDC.com');
      insert usr;
  }
}

Let us see how we can fix it

public class MixedDMLErrorFixed {
  public static void myMethod() {
      Account a = new Account(Name='XploreSFDC');
      insert a;
      FutureDemo.insertUser();
  }
}

public class FutureDemo {
  @future
  public static void insertUser() {
      Profile p=[SELECT Id FROM Profile WHERE 
Name='Standard User'];
      UserRole r = [SELECT Id FROM UserRole WHERE Name='CEO'];
      User usr = new User(alias = 'XploreSFDC'
          email='XploreSFDC@XploreSFDC.com'
          emailencodingkey='UTF-8', lastname='Max'
          languagelocalekey='en_US'
          localesidkey='en_US', profileid = p.Id
userroleid = r.Id,
          timezonesidkey='America/Los_Angeles'
          username='XploreSFDC@XploreSFDC.com');
      insert usr;
  }
}

Few Point to keep in mind:

  • Future method execute asynchronously (whenever server is free it will execute in future context).
  • We should not declare @future method in Apex Trigger.
  • @future method should be always static.
  • @future method accepts only primitive data types (Integer, String, Boolean, Date, etc…) as parameters and it won’t accept non-primitive data types (sObject,Custom Objects and standard Objects etc.. ) as parameters.
  • @future method should not contain return type. 
  • From an apex trigger we can make only make asynchronous call outs. To make call out we should include “callout = true” beside the future @annotation.
  • We cannot perform synchronous call outs from Apex Trigger.

Monday, April 24, 2023

What Is The Use Of SeeAllData In Test Class In Salesforce

Annotation IsTest(SeeAllData=true) opens up data access to records in your organization. The IsTest(SeeAllData=true) annotation applies to data queries but doesn't apply to record creation or changes, including deletions. New and changed records are still rolled back in Apex tests even when using the annotation.

// All test methods in this class can access all data.
@IsTest(SeeAllData=true)
public class TestDataAccessClass {
    // This test accesses an existing account. 
    // It also creates and accesses a new test account.
    static testmethod void myTestMethod1() {
        // Query an existing account in the organization. 
        Account a = [SELECT IdName FROM Account WHERE 
Name='Acme' LIMIT 1];
        System.assert(a != null);
        
     // Create a test account based on the queried account.
        Account testAccount = a.clone();
        testAccount.Name = 'Acme Test';
        insert testAccount;
        
        // Query the test account that was inserted.
        Account testAccount2 = [SELECT IdName 
FROM Account 
                          WHERE Name='Acme Test' LIMIT 1];
        System.assert(testAccount2 != null);
    }
    // Like the previous method, this test method can also 
access all data
    // because the containing class is annotated with 
@IsTest(SeeAllData=true).
    @IsTest static void myTestMethod2() {
        // Can access all data in the organization.
   }
}

The below second example shows how to apply the @IsTest(SeeAllData=true) annotation on a test method. Because the test method’s class isn’t annotated, you have to annotate the method to enable access to all data for the method. The second test method doesn’t have this annotation, so it can access only the data it creates. In addition, it can access objects that are used to manage your organization, such as users.


@IsTest
private class ClassWithDifferentDataAccess {

    // Test method that has access to all data.
    @IsTest(SeeAllData=true)
    static void testWithAllDataAccess() {
        // Can query all data in the organization.      
    }
    
// Test method that has access to only the data it creates
  // and organization setup and metadata objects.
    @IsTest static void testWithOwnDataAccess() {
        // This method can still access the User object.
        // This query returns the first user object.
        User u = [SELECT UserName,Email FROM User LIMIT 1]; 
        System.debug('UserName: ' + u.UserName);
        System.debug('Email: ' + u.Email);
        
      // Can access the test account that is created here.
        Account a = new Account(Name='Test Account');
        insert a;      
        // Access the account that was just created.
        Account insertedAcct = [SELECT Id,Name FROM Account 
                              WHERE Name='Test Account'];
        System.assert(insertedAcct != null);
    }
}


  • If a test class is defined with the @IsTest(SeeAllData=true) annotation, the annotation applies to all its test methods. The annotation applies if the test methods are defined with the @IsTest annotation or with the (deprecated) testMethod keyword.
  • The @IsTest(SeeAllData=true) annotation is used to open up data access when applied at the class or method level. However, if the containing class has been annotated with @IsTest(SeeAllData=true), annotating a method with @IsTest(SeeAllData=false) is ignored for that method. In this case, that method still has access to all the data in the organization. Annotating a method with @IsTest(SeeAllData=true) overrides, for that method, an @IsTest(SeeAllData=false) annotation on the class.
  • @IsTest(SeeAllData=true) and @IsTest(IsParallel=true) annotations cannot be used together on the same Apex method.

Thursday, April 20, 2023

Free Salesforce Certification Vouchers

Hello All,

Please leave your email id in the comment box or mail at sfdev52@gmail.com

The voucher will sent to your email id.

Free Salesforce Certified Associate Exam- Valid till Apr 30, 2023
$140 off on $200 Exam- Valid till June 30, 2023


Monday, April 17, 2023

How To Avoid Recursion in Apex Trigger

Recursion is the process of executing the same Trigger multiple times to update the record again and again due to automation. There may be chances we might hit the Salesforce Governor Limit due to Recursive Trigger.

To avoid these kind of situation we can use public class static variable.


In RecursiveTriggerHandler class, we have a static variable which is set to true by default.

public class RecursiveTriggerHandler{
     public static Boolean recursiveCheck = true;
}

In following trigger, we are checking if static variable is true only then trigger runs. Also we are setting static variable to false when trigger runs for first time. So if trigger will try to run second time in same request then it will not run.

trigger SampleTrigger on Contact (after update){
  Set<String> accIdSet = new Set<String>();
  if(RecursiveTriggerHandler.recursiveCheck){
      RecursiveTriggerHandler.recursiveCheck = false;
      for(Contact conObj : Trigger.New){
          if(conObj.name != 'SFDC'){
              accIdSet.add(conObj.accountId);
          }
      }
  }
}


1. Trigger runs, recursiveCheck variable value will be checked.
2. In the first run, variable will be true.
3. Then recursiveCheck variable value will be set to false.
4. Next time same IF condition will fail and recursion is stopped.

Mostly Viewed