Can I prevent an insert to be rolled back even if my class (later) runs into an exception?

I do some custom logging in my code which basically means I write and update sobject records that contain custom log information.

The problem is whenever an exception is thrown in my class the already inserted SObjects are rolled back.

Can I force them to stay persisted?

Answer

This is possible, but first a word on transaction scope and management…

Transaction Scope: It is important to understand a bit more about how the platform handles the transaction for DML operations executed. By default if you don’t catch exceptions the platform will rollback any changes the request (controller action method etc) automatically. This helps avoid the situation where some records are written (those leading up to the error) and others not. If your planning on catching exceptions in your UI (as is most common in VF) or logging reasons you need to consider this.

Logging and Savepoints: The following illustrates both a simple logging pattern and how you might want to consider wrapping you logic up in Savepoint to manage the rollback consistently. The approach used here is to store up your log entries until the ‘finally’ block, then insert them into the database.

public with sharing class Logger 
{       
    public static void demo()
    {
        // Rollback to here if any errors occuring in the following
        System.Savepoint sp = Database.setSavepoint();
        try
        {
            // Perform some work
            Logger.log('Creating Test');
            Test__c test = new Test__c();
            insert test;
            Logger.log('Created Test');

            // Do some calculations that might generate exceptions...
            Integer bad = 0;
            bad = bad / 0;
            Logger.log('Done some calcs');

            // Perform some other work (which might also cause an exception)
            insert new Test_Child__c(Test__c = test.Id); 
            Logger.log('Created Test Child');
        }
        catch (Exception e)
        {
            // Log the exception
            Logger.log(e);  

            // Rollback everything done up until this point
            Database.rollback(sp);          
        }
        finally
        {   
            // Flush the log writing everything captured to the database        
            Logger.flush();
        }
    }

    private static List<Log__c> logs = new List<Log__c>();

    public static void log(String message)
    {
        logs.add(new Log__c(Message__c = message));     
    }

    public static void log(Exception e)
    {       
        logs.add(new Log__c(Message__c = e.getMessage(), StackTrace__c = e.getStackTraceString()));
    }

    public static void flush()
    {
        insert logs;    
    }
}

The following shows a successful log output and a failed one (with the divide by zero code in above).

enter image description here

Finally if your interested in a pattern that helps you better manage transactional scope (without having to liter your code with Savepoint logic), avoid the DML governor and a few other features, take a look at the Unit Of Work pattern described here.

NOTE: There are a few exceptions that cannot be caught in Apex (governors being the main example), in this case you cannot use your own logging approach as like it or not the platform will rollback everything here as the finally will never be called. You may want to consider using Subscriber Support access to view the platform debug logs in this cases. Maybe then even enhance the Logger.log method to also emit to System.debug (utilise LoggingLevel.Error will ensure messages are not truncated in larger logs).

Attribution
Source : Link , Question Author : Robert Sösemann , Answer Author : Andrew Fawcett

Leave a Comment