Salesforce Apex初心者向けチュートリアル
⚡ スマートサマリー
Apexプログラミングは、Salesforce開発者にオブジェクト指向で厳密な型付け言語を提供し、CRMに独自のビジネスロジックを追加できるようにします。クラス、トリガー、バッチジョブを支え、ガバナー制限によって共有マルチテナントプラットフォームの安定性が維持されます。

Salesforce の Apex とは何ですか?
Apex Apexは、SalesforceがSaaS(Software as a Service)およびCRM(Customer Relationship Management)アプリケーションの構築用に開発した、オブジェクト指向かつ厳密な型付けを持つプログラミング言語です。Apexは、バックエンドデータベースのサポートとクライアント/サーバーインターフェースを提供することで、開発者がサードパーティ製のSaaSアプリケーションを作成し、システムイベントにビジネスロジックを追加するのに役立ちます。
Apex は、開発者がボタンのクリック、関連レコードの更新、Visualforce ページなどのシステム イベントにビジネス ロジックを追加するのに役立ちます。Apex の構文は、 Java. Salesforce に登録する CRMの仕組みを学ぶため。
Apexプログラミング言語の特徴
Salesforce Apex の重要な機能は次のとおりです。
- Apexは、大文字と小文字を区別しない言語です。
- Apexを使用すると、sObjectレコードに対してINSERT、UPDATE、UPSERT、DELETEなどのDML操作を実行できます。
- Apexでは、SOQL(Salesforce Object Query Language)とSOSL(Salesforce Object Search Language)を使用してsObjectレコードをクエリできます。
- を作成することができます 単体テスト そしてそれを実行して検証します コードカバレッジ そして、Apexにおけるコードの効率性。
- Apex はマルチテナント環境で実行され、 Salesforce Salesforceは、ユーザーが共有リソースを独占することを防ぐためのガバナー制限を定義しています。Salesforceのガバナー制限を超えるコードはすべて失敗し、エラーが表示されます。
- SalesforceオブジェクトはApexのデータ型として使用できます。例:
Account acc = new Account();
ここでいう「アカウント」は、標準的なSalesforceオブジェクトです。
- Apex は Salesforce のリリースごとに自動的にアップグレードされます。
開発者はどのような場合にApexを選択すべきか?
Apexコードは、Salesforceが提供する既製のポイントアンドクリック機能では実装できないほど複雑なビジネスシナリオの場合にのみ記述すべきです。
以下は、Apexコードを記述する必要があるいくつかのシナリオです。
- Salesforce と他のアプリケーションを統合する Web サービスを作成します。
- sObjectにカスタム検証を実装する。
- DML操作が実行されたときに、カスタムApexロジックを実行します。
- Flowなどの既存の宣言型自動化ツールでは構築できない機能を実装するため。
- 設定する メールサービス 受信メールの内容、ヘッダー、添付ファイルを処理する。
Apexが適切な選択肢となるタイミングがわかったら、次のステップは、コードを保存した後に何が起こるかを理解することです。
Apexの業務構造
以下は、Apexコードにおける一連の動作の流れです。
- 開発者向けアクション: 開発者がコードをプラットフォームに保存すると、すべてのApexコードはApexランタイムインタープリタが理解できる一連の命令にコンパイルされ、これらの命令はメタデータとしてプラットフォームに保存されます。
- エンドユーザーの操作: ユーザーイベントによってApexコードが実行されると、プラットフォームサーバーはメタデータからコンパイル済みの命令を取得し、Apexインタープリタを通して実行してから結果を返します。
以下の図は、開発者とエンドユーザーの操作がLightning Platformアプリケーションサーバーとどのように連携するかを示しています。
Apex開発環境
Apexコードは、Salesforceのサンドボックス環境または開発者エディション組織のいずれかで開発できます。
以下に示すように、サンドボックス環境でコードを開発してから本番環境にデプロイするのがベストプラクティスです。
Apexコード開発ツール:SalesforceのすべてのエディションでApexコードを開発するために利用できるツールは以下の3つです。
- 開発者コンソール
- Visual Studio Code Salesforce Extension Pack を使用(これは廃止された Force.com IDE に代わるものです)
- Code Salesforce設定ユーザーインターフェースのエディター
環境が整ったところで、データ型から始めて、言語の構成要素を見ていきましょう。
Apexのデータ型
Apexでサポートされているデータ型は以下のとおりです。
プリミティブ
整数、 DoubleLong、Date、Datetime、Decimal、Time、Blob、String、ID、およびBooleanはプリミティブデータ型とみなされます。すべてのプリミティブデータ型は、参照渡しではなく値渡しされます。
コレクション
Apexでは、3種類のコレクションが利用可能です。
- リスト: これは、インデックスに基づいて順序付けられたプリミティブ、sObject、コレクション、またはApexオブジェクトのコレクションです。
- セット: 重複を含まない、固有の要素の順不同の集合。
- 地図: これは、単一の値にマッピングされる一意のキーの集合であり、その値はプリミティブ型、sObject、コレクション、またはApexオブジェクトのいずれかになります。
sオブジェクト
これは Salesforce の特殊なデータ型です。 の表に似ています。 SQL また、SQLの列に似たフィールドが含まれています。
列挙型
Enumは絶対値ですtract データ型は、指定された有限個の識別子のうちの 1 つの値を格納するものです。
クラス、オブジェクト、インターフェース
ユーザー定義のApexクラスとインターフェースもデータ型として使用できます。オブジェクトとは、Apexでサポートされている任意のデータ型のインスタンスを指します。
Apex 構文
変数宣言
Apexは厳密な型付け言語であるため、Apexでは変数をデータ型を指定して宣言することが必須です。
具体的な例を挙げますと、以下の通りです。
Contact con = new Contact();
ここでは、変数conはデータ型としてContactを指定して宣言されています。
SOQL クエリ
SOQLはSalesforce Object Query Languageの略です。SOQLはSalesforceデータベースからsObjectレコードを取得するために使用されます。例:
Account acc = [SELECT Id, Name FROM Account LIMIT 1];
上記のクエリは、Salesforceデータベースからアカウントレコードを取得します。
ループステートメント
ループ文は、リスト内のレコードを反復処理するために使用されます。反復回数は、リスト内のレコード数と同じです。例:
List<Account> listOfAccounts = [SELECT Id, Name FROM Account LIMIT 100];
// iteration over the list of accounts
for(Account acc : listOfAccounts){
//your logic
}
上記のコードスニペットでは、listOfAccounts は List データ型の変数です。
フロー制御ステートメント
フロー制御文は、特定の条件に基づいてコードの一部を実行したい場合に有効です。
具体的な例を挙げますと、以下の通りです。
List<Account> listOfAccounts = [SELECT Id, Name FROM Account LIMIT 100];
// execute the logic if the size of the account list is greater than zero
if(listOfAccounts.size() > 0){
//your logic
}
上記のコードスニペットは、データベースからアカウントレコードを照会し、リストのサイズを確認します。
DMLステートメント
DMLはデータ操作言語の略です。DMLステートメントは、Salesforceデータベース内のデータを操作するために使用されます。例:
Account acc = new Account(Name = 'Test Account'); insert acc; //DML statement to create account record.
Apexアクセス指定子
Apexでサポートされているアクセス指定子は以下のとおりです。
公共
このアクセス指定子は、名前空間内でApexが使用するクラス、メソッド、または変数へのアクセスを許可します。
プライベート
このアクセス指定子を使用すると、クラス、メソッド、または変数を、それが定義されているコードセクション内、つまりローカルでのみ使用できます。アクセス指定子が定義されていないすべてのメソッドと変数には、デフォルトのアクセス指定子として private が使用されます。
保護されました
このアクセス指定子により、定義元のApexクラス内の任意の内部クラスが使用するメソッドまたは変数へのアクセスが可能になります。
このアクセス指定子を使用すると、名前空間内だけでなく名前空間外でも、Apex が使用するクラス、メソッド、または変数にアクセスできるようになります。必要がない限り、global キーワードを使用しないのがベストプラクティスです。
Apex のキーワード
共有あり
このキーワードを使用してクラスが定義されている場合、現在のユーザーに適用されるすべての共有ルールが適用されます。このキーワードが存在しない場合、コードはシステムコンテキストで実行されます。
具体的な例を挙げますと、以下の通りです。
public with sharing class MyApexClass{
// sharing rules enforced when code in this class executes
}
共有せずに
このキーワードを使用してクラスが定義されている場合、現在のユーザーに適用される共有ルールは適用されません。
具体的な例を挙げますと、以下の通りです。
public without sharing class MyApexClass{
// sharing rules are not enforced when code in this class executes
}
静的
staticキーワードで定義された変数またはメソッドは、一度初期化されてクラスに関連付けられます。静的変数とメソッドは、クラスのインスタンスを作成することなく、クラス名で直接呼び出すことができます。
終
finalキーワードで定義された定数またはメソッドはオーバーライドできません。例:
public class myCls {
static final Integer INT_CONST = 10;
}
この INT_CONST 変数の値を上書きしようとすると、System.FinalException: Final 変数は既に初期化されています、という例外が発生します。
返品
このキーワードはメソッドから値を返します。 例えば:
public String getName() {
return 'Test';
}
ヌル
これはヌル定数を定義し、変数に代入することができます。例:
Boolean b = null;
バーチャル
クラスがvirtualキーワードで定義されている場合、そのクラスは拡張およびオーバーライドすることができます。
抽象
クラスがabsで定義されている場合tract キーワードには、少なくとも 1 つの abs キーワードを含むメソッドが含まれている必要があります。tract、そしてそのメソッドはシグネチャのみを持つべきです。
具体的な例を挙げますと、以下の通りです。
public abstract class MyAbstractClass {
abstract Integer myAbstractMethod1();
}
頂点文字列
文字列とは、文字数に制限のない文字の集合です。例:
String name = 'Test';
SalesforceのStringクラスには、いくつかの組み込みメソッドが用意されています。以下に、よく使用されるメソッドをいくつか示します。
省略形(最大幅)
このメソッドは、文字列を指定された長さに切り詰め、指定された長さより長い場合は切り詰めた文字列を返します。そうでない場合は、元の文字列を返します。maxWidth 変数の値が 4 未満の場合、このメソッドは実行時例外 System.StringException: 最小略語幅は 4 です をスローします。
具体的な例を挙げますと、以下の通りです。
String s = 'Hello World';
String s2 = s.abbreviate(8);
System.debug('s2: ' + s2); //Hello...
Capitalize()
このメソッドは、文字列の最初の文字をタイトルケースに変換して返します。
具体的な例を挙げますと、以下の通りです。
String s = 'hello';
String s2 = s.capitalize();
System.assertEquals('Hello', s2);
含む(部分文字列)
このメソッドは、メソッドを呼び出す文字列に指定された部分文字列が含まれている場合にtrueを返します。
String name1 = 'test1';
String name2 = 'test';
Boolean flag = name1.contains(name2);
System.debug('flag:: ' + flag); //true
等しい(文字列またはID)
このメソッドは、渡されたパラメータが null ではなく、かつメソッド呼び出し元の文字列と同じバイナリ文字シーケンスを表している場合に true を返します。
ID値を比較する場合、IDの長さが等しくない場合があります。たとえば、15文字のIDを表す文字列と18文字のIDを表すオブジェクトを比較した場合でも、このメソッドはtrueを返します。例:
String stringValue15 = '001D000000Ju1zH';
Id idValue18 = '001D000000Ju1zHIAR';
Boolean result = stringValue15.equals(idValue18);
System.debug('result: ' + result); //true
上記の例では、equals メソッドは 15 文字のオブジェクト ID と 18 文字のオブジェクト ID を比較し、両方の ID が同じバイナリ シーケンスを表している場合は true を返します。
大文字と小文字を区別して比較するには、このメソッドを使用します。
escapeSingleQuotes(stringToEscape)
このメソッドは、文字列内の単一引用符の前にエスケープ文字(\)を追加し、結果を返します。このメソッドは、動的なSOQLクエリを作成する際にSOQLインジェクションを防止します。すべての単一引用符がデータベースコマンドではなく、囲み文字列として扱われることを保証します。
具体的な例を挙げますと、以下の通りです。
String s = 'Hello \'Tom\''; String escapedStr = String.escapeSingleQuotes(s); System.debug(escapedStr); // Outputs Hello \'Tom\'
削除(部分文字列)
このメソッドは、メソッドを呼び出した文字列から指定された部分文字列をすべて削除し、結果の文字列を返します。
具体的な例を挙げますと、以下の通りです。
String s1 = 'Salesforce and force.com';
String s2 = s1.remove('force');
System.debug('s2: ' + s2); // 'Sales and .com'
部分文字列(開始インデックス)
このメソッドは、startIndexで指定された文字から始まり、文字列の末尾まで続く部分文字列を返します。
具体的な例を挙げますと、以下の通りです。
String s1 = 'hamburger';
String s2 = s1.substring(3);
System.debug('s2: ' + s2); //burger
逆行する ()
このメソッドは文字列のすべての文字を反転させて返します。例:
String s = 'Hello';
String s2 = s.reverse();
System.debug('s2:::: ' + s2); // olleH
trim()
このメソッドは、文字列の先頭と末尾にあるすべての空白文字を削除して返します。
valueOf(変換対象)
このメソッドは、渡されたオブジェクトの文字列表現を返します。
文字列、クラス、キーワードは、ロジックを再利用可能な単位にパッケージ化する際に組み合わさるものであり、まさにそれがApexクラスの目的です。
アペックスクラス
Apexクラスは、オブジェクトを作成するための設計図またはテンプレートです。オブジェクトはクラスのインスタンスです。
SalesforceでApexクラスを作成する方法は3つあります。
- 開発者コンソール
- Visual Studio Code Salesforce Extension Packを使用
- 設定のApexクラス詳細ページ
Apexでは、トップレベルクラスとも呼ばれる外側のクラスを定義できるだけでなく、外側のクラスの中に内側のクラスと呼ばれるクラスを定義することもできます。
外部クラスの宣言では、global や public などのアクセス修飾子を使用することが必須です。
内部クラスの宣言では、アクセス修飾子を使用する必要はありません。
Apexクラスは、classキーワードの後にクラス名を記述することで定義されます。
`extends` キーワードは、Apex クラス内で既存のクラスを拡張するために使用され、`implements` キーワードは、Apex クラス内でインターフェースを実装するために使用されます。
Salesforce Apexは多重継承をサポートしていません。Apexクラスは既存のApexクラスを1つしか継承できませんが、複数のインターフェースを実装することは可能です。
Apexクラスにはユーザー定義コンストラクタを含めることができ、ユーザー定義コンストラクタが利用できない場合は、デフォルトコンストラクタが使用されます。コンストラクタ内のコードは、クラスのインスタンスが作成されるときに実行されます。
Apexクラスの構文:
public class myApexClass{
// variable declaration
//constructor
public myApexClass(){
}
//methods declaration
}
新しいキーワードは、Apexクラスのインスタンスを作成するために使用されます。Apexクラスのインスタンスを作成するための構文は以下のとおりです。
myApexClass obj = new myApexClass();
Apex ゲッターとセッター
Apex プロパティは Apex 変数に似ています。Apex プロパティにはゲッターとセッターが必要です。これらは、プロパティ値にアクセスまたは変更する前にコードを実行するために使用できます。get アクセサ内のコードは、プロパティ値が読み取られたときに実行されます。set アクセサ内のコードは、プロパティ値が変更されたときに実行されます。get アクセサのみを持つプロパティは読み取り専用、set アクセサのみを持つプロパティは書き込み専用、get アクセサと set アクセサの両方を持つプロパティは読み書き可能とみなされます。Apex プロパティの構文:
public class myApexClass {
// Property declaration
access_modifier return_type property_name {
get {
//code
}
set{
//code
}
}
}
ここで、access_modifierはプロパティのアクセス修飾子、return_typeはプロパティのデータ型、property_nameはプロパティの名前です。
以下は、get アクセサと set アクセサの両方を持つ Apex プロパティの例です。
public class myApex{
public String name{
get{ return name; }
set{ name = value; }
}
}
ここで、プロパティ名は name であり、パブリックプロパティであり、String 型のデータを返します。
getブロックとsetブロックにコードを記述する必要はありません。これらのブロックは空のままにして、自動プロパティを定義することができます。例:
public double MyReadWriteProp{ get; set; }
取得アクセサと設定アクセサは、独自のアクセス修飾子を指定して定義することもできます。アクセサが修飾子を指定して定義されている場合、プロパティのアクセス修飾子が上書きされます。例:
public String name{private get; set;}// name is private for read and public for write.
クラスは、明示的に呼び出す再利用可能なロジックを定義します。次に説明するトリガーは、レコードが変更されるたびに自動的に実行されます。
アペックストリガー
Apexトリガーを使用すると、DML操作の実行前と実行後にカスタムApexを実行できます。
Apex は以下の 2 種類のトリガーをサポートしています。
トリガーの前に: これらのトリガーは、レコードがデータベースに保存される前に、フィールドの値を検証および更新するために使用されます。
トリガー後: これらのトリガーは、レコードがデータベースにコミットされた後にシステムによって設定されるフィールド値(レコードIDやLastModifiedDateフィールドなど)にアクセスするために使用されます。これらのフィールド値は、他のレコードを変更するために使用できます。トリガー実行後に発生するレコードは読み取り専用です。
一括処理トリガーを作成するのがベストプラクティスです。一括処理トリガーは、単一のレコードだけでなく、複数のレコードを一度に処理できます。
Apexトリガーの構文:
trigger TriggerName on ObjectName (trigger_events) {
//Code_block
}
ここで、TriggerNameはトリガーの名前、ObjectNameはトリガーが書き込まれるオブジェクトの名前、trigger_eventsはカンマ区切りのイベントリストです。
Apexトリガーでサポートされているイベントは次のとおりです。挿入前、更新前、削除前、挿入後、更新後、削除後、削除解除後。
静的キーワードはApexトリガーでは使用できません。内部クラスに適用可能なキーワードはすべてApexトリガーで使用できます。
すべてのトリガーには、実行時コンテキストを返す暗黙的な変数が定義されています。これらの変数は System.Trigger クラスで定義され、コンテキスト変数と呼ばれます。以下の 2 つのスクリーンショットは、Apex トリガーでサポートされているコンテキスト変数の一覧です。
Apexトリガーにおけるコンテキスト変数に関する考慮事項は以下のとおりです。
- DML操作では、trigger.newとtrigger.oldを使用しないでください。
- Trigger.new は削除できません。
- Trigger.new は、after トリガーでは読み取り専用です。
- Trigger.new は、before トリガー内でのみ、同じオブジェクトのフィールドの値を変更するために使用できます。
以下の2つのスクリーンショットは、さまざまなトリガーイベントにおける特定のアクションに関する考慮事項を示しています。
トリガーはリアルタイムロジックを処理しますが、一部のジョブは単一のトランザクションでは処理しきれないほど大規模です。そこでバッチApexの出番となります。
Apexのバッチクラス
Salesforceのバッチクラスは、通常の方法で処理するとApexガバナーの制限を超えてしまうような大量のレコードを処理するために使用されます。バッチクラスはコードを非同期的に実行します。
バッチクラスの利点は以下のとおりです。
- バッチクラスはデータをチャンク単位で処理し、あるチャンクの処理が失敗した場合でも、正常に処理されたチャンクはロールバックされません。
- バッチクラス内のデータチャンクはそれぞれ新しいガバナー制限セットで処理され、コードがガバナーの実行制限内で実行されることが保証されます。
バッチクラスとして使用するには、ApexクラスがDatabase.Batchableインターフェースを実装する必要があります。このインターフェースは、バッチクラスが実装しなければならない3つのメソッドを提供します。
1. 開始()
このメソッドは、インターフェース メソッド execute によって処理されるレコードまたはオブジェクトのスコープを生成します。バッチの実行中に一度だけ呼び出されます。このメソッドは、Database.QueryLocator オブジェクトまたは Iterable を返します。Database.QueryLocator オブジェクトを使用した SOQL クエリで取得できるレコード数は 50 万件ですが、Iterable を使用した場合、SOQL クエリで取得できるレコードの総数は 50,000 件のみです。Iterable は、バッチ クラスの複雑なスコープを生成するために使用されます。
startメソッドの構文:
global (Database.QueryLocator | Iterable<sObject>) start(Database.BatchableContext bc) {}
2. 実行()
このメソッドは、データの各チャンクを処理するために使用されます。execute メソッドは、レコードの各チャンクに対して呼び出されます。実行時のデフォルトのバッチサイズは 200 レコードです。execute メソッドは 2 つの引数を取ります。
Database.BatchableContext オブジェクトへの参照、
sObject のリスト (例: List)または、パラメータ化された型のリスト。execute メソッドの構文:
global void execute(Database.BatchableContext bc, List<P> records){}
3. 終了()
finish メソッドは、バッチクラスの実行中に一度だけ呼び出されます。finish メソッド内では、後処理操作を実行できます。例えば、確認メールを送信するなどです。このメソッドは、すべてのバッチの処理が完了したときに呼び出されます。finish メソッドの構文は次のとおりです。
global void finish(Database.BatchableContext bc){}
Database.BatchableContext オブジェクト
Database.Batchable インターフェイスの各メソッドは、Database.BatchableContext オブジェクトへの参照を持っています。
このオブジェクトは、 track バッチジョブの進行状況。
以下は、BatchableContext が提供するインスタンスメソッドです。
- getChildJobId(): このメソッドは、現在処理中のバッチジョブのIDを返します。
- getJobId(): このメソッドはバッチ ジョブの ID を返します。
以下はバッチクラスの構文です。
global class MyBatchClass implements Database.Batchable<sObject> {
global (Database.QueryLocator | Iterable<sObject>) start(Database.BatchableContext bc) {
// collect the batches of records or objects to be passed to execute
}
global void execute(Database.BatchableContext bc, List<P> records){
// process each batch of records
}
global void finish(Database.BatchableContext bc){
// execute any post-processing operations
}
}
Database.executeBatch メソッド
Database.executeBatch メソッドは、バッチクラスを実行するために使用されます。
このメソッドは、処理対象のバッチクラスのインスタンスと、バッチサイズを指定するためのオプションのスコープパラメータの2つのパラメータを受け取ります。スコープパラメータが指定されていない場合は、デフォルト値の200が使用されます。
Database.executeBatch の構文:
Database.executeBatch(myBatchObject, scope)
MyBatchClassという名前のバッチクラスを実行します。
MyBatchClass myBatchObject = new MyBatchClass(); Id batchId = Database.executeBatch(myBatchObject, 100);
データベース。ステートフル
バッチクラスはデフォルトではステートレスです。executeメソッドが呼び出されるたびに、オブジェクトの新しいコピーが取得され、クラスのすべての変数が初期化されます。
Database.Stateful は、バッチクラスをステートフルにするために実装されています。
バッチ クラスが実装している場合 データベースステートフルインターフェースでは、すべてのインスタンス変数は値を保持しますが、静的変数はトランザクション間でリセットされます。
バッチ処理を超えた非同期Apex
バッチApexは、コードを非同期で実行する複数の方法のうちの1つにすぎません。Salesforceは4つの非同期オプションを提供しており、適切なオプションを選択するには、処理するデータ量や、ジョブを連鎖させる必要があるか、スケジュールする必要があるかなどを考慮する必要があります。
- 今後の方法: @future で注釈付けされたこれらは、外部ウェブサービスへの呼び出しなど、単純な実行して放置する操作に最適です。
- キュー可能なApex: Queueableインターフェースを実装し、複雑なオブジェクト型を受け入れ、ジョブIDを返し、ジョブを連鎖させることをサポートします。
- バッチApex: 上記のように、非常に大きなデータ量をチャンク単位で処理します。
- 予定された頂点: Schedulableインターフェースを実装することで、クラスを特定の時間に実行できるようにします。例えば、毎晩のクリーンアップジョブなどです。
| タイプ | 以下のためにベスト | 主な機能 |
|---|---|---|
| 未来法 | シンプルなコールアウト | 撃ちっぱなし |
| キュー可能なApex | 順次処理 | ジョブチェーンとモニタリング |
| バッチApex | 何百万ものレコード | チャンク処理 |
| スケジュールされたApex | 定期的な仕事 | Cronベースのタイミング |
どの実行モデルを選択しても、すべてのトランザクションは、以下に示すプラットフォーム全体のガバナー制限に基づいて計測されます。
アペックスガバナ制限
Apex ガバナー制限とは、Apex ランタイム エンジンによって強制される制限であり、暴走した Apex コードやプロセスが共有リソースを独占したり、マルチテナント環境における他のユーザーの処理を妨害したりしないようにするためのものです。これらの制限は、各 Apex トランザクションに対して検証されます。Salesforce が各 Apex トランザクションに対して定義するガバナー制限は以下のとおりです。
| 詳細説明 | リミット |
|---|---|
| 同期トランザクションで実行できるSOQLクエリ | 100 |
| 非同期トランザクションで実行可能なSOQLクエリ | 200 |
| SOQL クエリで取得できるレコード | 50,000 |
| Database.getQueryLocator で取得できるレコード | 10,000 |
| Apexトランザクションで実行できるSOSLクエリ | 20 |
| SOSL クエリで取得できるレコード | 2,000 |
| Apexトランザクションで実行できるDMLステートメント | 150 |
| DML ステートメント、Approval.process、database.emptyRecycleBin の結果として処理できるレコード | 10,000 |
| Apexトランザクションで実行できるコールアウト | 100 |
| Apexトランザクションで実行されるすべてのコールアウトに対する累積タイムアウト制限 | 120 seconds |
| System.enqueueJob を使用してキューに追加できる Apex ジョブの数に制限があります。 | 50 |
| 各 Apex トランザクションの実行時間制限 | 10 minutes |
| Apexクラスで使用できるキャラクターとトリガーの制限 | 1億円 |
| 同期トランザクションのCPU時間制限 | 10,000ミリ秒 |
| 非同期トランザクションのCPU時間制限 | 60,000ミリ秒 |
| 合計ヒープサイズ | 6MB(同期)/12MB(非同期) |
Apexでテストクラスを作成する方法
Salesforceでは、Apexコードを本番環境にデプロイする前に、少なくとも75%のコードが単体テストでカバーされている必要があり、すべてのトリガーにも何らかのテストカバレッジがなければなりません。したがって、テストクラスの作成は、オプションではなく、Apexの必須スキルです。
テストクラスには@isTestアノテーションが付与され、各テストメソッドにも@isTestアノテーションが付与されます。テストメソッドはデータベースにデータをコミットせず、既存の組織データのほとんどを参照しないため、各テストは独自のレコードを作成します。Test.startTest()メソッドとTest.stopTest()メソッドは、テスト対象コードに新たなガバナー制限を適用し、アサーションによってロジックが期待どおりに動作したことを検証します。
アカウント作成ロジックの簡単なテストクラスを以下に示します。
@isTest
private class AccountHandlerTest {
@isTest
static void testCreateAccount() {
Account acc = new Account(Name = 'Test Account');
Test.startTest();
insert acc;
Test.stopTest();
Account result = [SELECT Id, Name FROM Account WHERE Id = :acc.Id];
System.assertEquals('Test Account', result.Name);
}
}
テストを作成する際は、以下のガイドラインに従ってください。
- @testSetup メソッドを使用すると、クラス内のすべてのテストメソッドに対して共有テストデータを一度作成できます。
- 一括処理の動作をテストするには、1件だけでなく200件のレコードを挿入してください。
- 肯定的なシナリオ、否定的なシナリオ、および制限付きユーザーのシナリオを網羅する。
- 必ず意味のある System.assert ステートメントを含めてください。アサーションのないカバレッジは何も証明しません。
Apexのベストプラクティス
初心者は、1件のレコードには対応できるものの、実際の大量処理では失敗するApexコードを書いてしまいがちです。以下のプラクティスに従うことで、コードをガバナー制限内に収め、保守性を向上させることができます。
- すべてをまとめて処理する: トリガーは一度に最大200件のレコードを受け取る可能性があるため、レコードのコレクションを処理するロジックを記述してください。
- SOQLとDMLはループ処理から除外してください。 ループの前にクエリを実行し、変更内容をリストに収集し、ループの後に1つのDMLステートメントを実行します。
- オブジェクトごとに1つのトリガー: トリガーにはロジックを組み込まず、処理をハンドラークラスに委任することで、実行順序を予測可能にする。
- 共有して使用する: システムコンテキストで実行する必要がある明確な理由が文書化されていない限り、レコードレベルのセキュリティを適用してください。
- IDをハードコーディングすることは避けてください。 サンドボックス環境と本番環境ではレコードIDが異なるため、レコードIDをクエリするか、カスタムメタデータを使用してください。
- コード内の制限を監視する: Limits クラスのメソッド(Limits.getQueries() など)を使用すると、実行時のリソース消費量を確認できます。
💡 ヒント: デプロイ前に、サンドボックス環境で200件のレコードに対してコードを実行してください。ガバナー制限の違反のほとんどは、大量のデータ処理時にのみ発生するため、本番環境でデバッグするよりも、早期に発見する方がはるかにコスト効率が良いです。






