Too Many SOQL Queries: 101を回避する



+ [Auraを使用する際のjavascript側のpromise実行順序制御について](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007erTOySAM)


>実はjavascript側で処理をforで回したいのはSOQLのガバナ制限を回避したいという意図があります。
APEX内で一度に処理するとガバナ制限を超えてしまうことが確実のなのでjavascript側で一度に処理する量を制御して、都度APEXをコールしてSOQL処理をしたいと考えています。
その為APEXに潜って一度に全て処理をするのではなく、ガバナ制限を迎える毎にjavascript側に息継ぎに戻りたいです。

ということなので、Auraコンポーネントから呼び出すApexクラスのメッソドの中で複数のメッソドを呼び出した場合にToo Many SOQL Queries: 101のエラーにならないようにできれば解決できそうです。

まずは、Auraコンポーネントから呼び出すメッソドを作成します。

getData()でSOQLを50回、getData2()を51回呼び出します。

```
    @AuraEnabled
    public static Map<String,Object> getInitSearch() {
        Map<String,Object>  RetMap = new Map<String,Object>();
        RetMap.put('myData',getData ());
        RetMap.put('myData2',getData2 ());
        return RetMap;
    }
```

そうすると、期待通りにToo Many SOQL Queries: 101になりました。

お決まりのエラーですが、ちゃんと調べてみると太字の定義が今一つ理解できません。

+ **ひとつの呼び出し、または、コンテキスト**から発動されたトリガ内のすべての SOQL クエリは、100 の制限にカウントされます。

https://help.salesforce.com/s/articleView?id=000386220&type=1

もう少し調べてみると 一つのトランザクションって書いてありますね。

The “Too Many SOQL Queries: 101” error in Salesforce occurs when a piece of Apex code performs more than 100 SOQL (Salesforce Object Query Language) queries in **a single transaction**. 

https://www.crsinfosolutions.com/how-to-resolve-the-too-many-soql-queries-101-error-in-salesforce/

Apexでトランザクションと言えば、以下の構文です。

```
try
{
}
catch (DmlException e)
{
}
```

そこで、先程の２つのメッソドに上記構文を追加します。

```
    public static List<Map<String,Object>> getData () {    
        List<Map<String,Object>> myDataList = new List<Map<String,Object>>();
        try
        {
            String mySOQL = 'SELECT Id,Name FROM  Account Limit 1 ' ; 
            List<Account> myAccountList = new List<Account>();
            for (Integer i = 0, j = 0; i < 50; i++) {
                myAccountList = Database.query(mySOQL);
            }
            String Name_st ='';
            Integer I = 0;
            for (Account myobj : myAccountList) {
                Map<String, Object> myMap = new Map<String, Object>();        	
                myMap.put('Id',myObj.Id);            
                myMap.put('Name',myObj.Name);
                myDataList.add(myMap);
                system.debug(Logginglevel.INFO,'### whereString ############ ----> '+ myDataList );
            }
        }

        catch (DmlException e)
        {
            String stackTrace = e.getStackTraceString();
            if (stackTrace.contains('AnonymousBlock'))
                system.debug('Execute Anonymous');
            // other parsing...
        }
        return myDataList;
    }
```

そうすると、51回と50回SOQLを呼び出しましたが、エラーになりません。きっとトランザクションの中で101回未満なので制限を回避できていますね。

# 検証用のコンポーネント


コンポーネント
```
<aura:component controller="ApexLimitTest" implements="force:appHostable" access="global" >
	<!-- ************************************************* -->
    <!-- Apex ガバナ制限テスト（タブをつけてメニューに登録)-->
    <!--  K.Otsubo 2024/09/10                              -->
    <!-- ************************************************* -->
    
    <!-- 静的リソース -->
    <ltng:require scripts="{!$Resource.fkdutil}" afterScriptsLoaded="{!c.fncJsLoaded}" />
    
    <!-- 画面ロード時のイベントがあればここに書く          -->  
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    
    <aura:attribute name="myData" type="List" />   
    <aura:attribute name="message" type="String" />
     
    <div class="slds-box slds-box_xx-small slds-text-align_left slds-m-around_xx-small slds-theme_default">
        
        {!v.message}
        <!-- {!v.myData} -->
     
        
        <lightning:button variant="brand" label="テスト" onclick="{!c.doInit}"                                          
                                          iconName="utility:file" iconPosition="left"
                                          aura:id="myId010" name="myId010"/>  
        
        <aura:iteration items="{!v.myData}" var="detail" indexVar="sNo">
            {!sNo} : {!detail.Id} : {!detail.Name}
        </aura:iteration>
    </div>
    
</aura:component>
```

コントローラ
```
({
	//静的リソース読み取り終了処理：初期処理の後に実行される
	fncJsLoaded: function(component, event, helper) {
		console.log('debug---fncJsLoaded');		
		return;
	},
    
	doInit : function(component, event, helper) {    
        //初期値セット 
		var action = component.get("c.getInitSearch");
        //action.setParams({"whereString":whereString});
        action.setCallback(this, function(response){
            if (action.getState() == "SUCCESS") {            	
                var myVal = response.getReturnValue();
                component.set("v.myData",myVal.myData);                                
            }  else if (action.getState() == "ERROR" ) {
                var errors = response.getError();
                if (errors[0] && errors[0].message) {
                    // サーバーサイドでcatchできなかったパターン
                	component.set("v.message", errors[0].message);
                } 
            }
        });
        $A.enqueueAction(action);
    },
})
```

# 関連する資料

+ [Dynamically Determine Calling Context?](https://salesforce.stackexchange.com/questions/131140/dynamically-determine-calling-context)