-2

I have a simple LWC where a user will enter a list of Book titles (on separate lines) into a text area. I then send those books back to apex in a List where I insert the records using the title.

I've been able to successfully insert the list of records, however, if the number of books records is high, I'm getting a "too many SOQL" errors due to other trigger processes happening in the background.

The solution I had in mind was to modify my LWC or Apex so that if a user enters in hundreds of book titles, then only insert a few at a time.

For example:

  1. User enters in 500 book titles and clicks "Create Books"
  2. Send 50 books back to my apex controller
  3. Those 50 books get successfully inserted
  4. Continue Steps 2-3 until all books are inserted

createBooks.html

<template>
  <div>
    <lightning-textarea label="Books:" onchange={handleBookTitles}></lightning-textarea>
  </div>
  <div>
    <lightning-button label="Create Books" title="Create Books" onclick={handleButton}></lightning-button>
  <div>
</template>

createBooks.js

import { LightningElement} from 'lwc';
import InsertBooks from '@salesforce/apex/BooksController.InsertBooks';

export default class InsertBooksLWC extends LightningElement {

bookTitles;

handleButton() {

    isInserted({ data: JSON.stringify(this.bookTitles) }).then(result => {
        if (result) {
            //books were inserted
        } else {
            //books were NOT inserted
        }


    }).catch(error => {
        //error
    });
}

handleBookTitles(event) {
    this.bookTitles = event.detail.value.split('\n');
}
}

BooksController.cls

public with sharing class BooksController {

@AuraEnabled
public static Boolean isInserted(List<String> books) {

    List<Books__c> booksToInsert = new List<Books__c>();

    for (Books__c b : books){
        booksToInsert.add(b.Title__c);
    }

    if (!booksToInsert.isEmpty()) {
        insert booksToInsert;
        return true;
    }

    return false;
}
}
2
  • 1
    Please edit your post to describe what you have tried and the specific error or problem you have encountered. Jan 6, 2023 at 15:15
  • @DavidCheng Done, thanks Jan 6, 2023 at 16:09

2 Answers 2

2

Please try by below JS

Explaination:

  1. Store the data in bookTitles variable

  2. Divide that array into 50 by using reduce function

  3. Call backend on each 50 records array.

    import { LightningElement} from 'lwc';
    import InsertBooks from '@salesforce/apex/BooksController.InsertBooks';
    const PER_CHUNK = 50;
    export default class InsertBooksLWC extends LightningElement {
    
    bookTitles;
    
    handleButton() {
    
    const booksByChunk  = bookTitles.reduce((all,one,i) => {
           const ch = Math.floor(i/PER_CHUNK); 
           all[ch] = [].concat((all[ch]||[]),one); 
           return all
        }, []);
    
       booksByChunk.forEach((books)=>{
            isInserted({ data: books }).then(result => {
                if (result) {
                    //books were inserted
                } else {
                    //books were NOT inserted
                }
            }).catch(error => {
                //error
            });
        })
    
    }
    
        handleBookTitles(event) {
            this.bookTitles = event.detail.value.split('\n');
        }
    }
    
0

Salesforce has an immense database underlying its platform and this calls for some mechanism to put check on what amount of data processing we can do in a given transaction. It controls and handles all DML operations that are executed from a non-anonymous(apex class) context using internal limits called the governor limits.

In cases, where you are not sure, that scale of data you will be processing - it is a good practice to write a batch Apex and configure your apex class to delegate a method that will pass all records(call) to the batch apex for processing.

public with sharing class BooksController {

@AuraEnabled
public static void isInserted(List<String> books) {// Delegator, does no other job than sending all the bookNames to batch apex
 

        batchClassNameHere runningbatch= new batchClassNameHere (books);  
        database.executeBatch(runningbatch,200);
 

    

}    
}

The batch apex must be composed as a standard Salesforce batch with a constructor to receive the books list!

    global class batchClassNameHere Database.Batchable<sObject>{

    private List<String> bookNames;
    private List<Books__c> books;

    public batchClassNameHere (List<String> newbooks) {//parameterized constructor for receiving book names.
        books= new List<String>newbooks;
    }

    global Database.QueryLocator start(Database.BatchableContext BC) {
      
    }

    global void execute(Database.BatchableContext BC, List<String> bookNames) {
// use the list of bookNames to process your data followed by Database.execute() to run this batch            
    }
    }   

    global void finish(Database.BatchableContext BC) {
    }
}

This is a pattern of programming that can handle any number of records at all times. Thanks.

1
  • Was trying to avoid the batch route, but I appreciate your answer! Although, I may have to revisit this depending on how it goes Jan 7, 2023 at 16:28

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .