DML を使用してレコードを操作する
学習の目的
この単元を完了すると、次のことができるようになります。
- DML を使用してレコードを挿入、更新、削除する。
- DML ステートメントを一括実行する。
- upsert を使用してレコードを挿入または更新する。
- DML 例外をキャッチする。
- データベースメソッドを使用して部分的な完了オプションを指定し、新規レコードを挿入して結果を処理する。
- DML ステートメントを使用する場合とデータベースメソッドを使用する場合を理解する。
- 関連レコードに対して DML 操作を実行する。
DML を使用してレコードを操作する
Salesforce でのレコードの作成や変更には、データ操作言語 (DML) を使用します。DML では、シンプルなステートメントを使用してレコードの挿入、更新、マージ、削除、復元を行うことで、簡単にレコードを管理できます。
Apex はデータ指向の言語であり、Lightning Platform に保存されるため、Salesforce のデータに直接アクセスできます。データソースに接続するために追加の設定が必要な他のプログラミング言語とは異なり、Apex DML ではレコードの管理が容易です。DML ステートメントをコールすることで、Salesforce レコードに対する操作をすばやく実行できます。次の例では、取引先 Acme を Salesforce に追加します。最初に取引先 sObject が作成され、引数として insert ステートメントに渡されます。これにより、レコードが Salesforce 内に保持されます。
// Create the account sObject Account acct = new Account(Name='Acme', Phone='(415)555-1212', NumberOfEmployees=100); // Insert the account by using DML insert acct;
DML ステートメント
次の DML ステートメントを使用できます。
insert
update
upsert
delete
undelete
merge
各 DML ステートメントは 1 つの sObject または sObject のリスト (または配列) を受け入れます。レコードを処理する場合は、sObject のリストを操作する方が効率的です。
一部を除き、これらのステートメントは使い慣れたデータベース操作です。upsert
ステートメントと merge
ステートメントは Salesforce 固有で、非常に便利です。
upsert
DML 操作では、既存のオブジェクトが存在するかどうかを判別するために、指定された項目を使用するか、項目が指定されていない場合は ID 項目を使用して、1 つのステートメント内で新規レコードの作成や sObject レコードの更新を行います。
merge
ステートメントは、同じ sObject データ型の最大 3 つのレコードを 1 つのレコードにマージし、他のレコードを削除してから、関連レコードを再ペアレント化します。
新規レコードへの ID 項目の自動割り当て
レコードの挿入時、システムによって各レコードに ID が割り当てられます。データベースに ID 値が保持されるだけでなく、DML コールの引数として使用した sObject 変数にも ID 値が自動入力されます。
次の例は、挿入された取引先に対応する sObject の ID を取得する方法を示します。
// Create the account sObject Account acct = new Account(Name='Acme', Phone='(415)555-1212', NumberOfEmployees=100); // Insert the account by using DML insert acct; // Get the new ID on the inserted sObject argument ID acctID = acct.Id; // Display this ID in the debug log System.debug('ID = ' + acctID); // Debug log result (the ID will be different in your case) // DEBUG|ID = 001D000000JmKkeIAF
一括 DML
DML 操作は、単一の sObject で行うことも、sObject のリストで一括で行うこともできます。一括 DML 操作ではガバナ制限 (Apex トランザクションごとのステートメント数を 150 件に制限する DML 制限など) に達することを防止できるため、この操作を実行することをお勧めします。この制限は、Lightning Platform の共有リソースに公正にアクセスできるようにするために設定されています。sObject のリストで DML 操作を実行すると、sObject ごとに 1 つのステートメントとみなされるのではなく、1 つの DML ステートメントとみなされます。
次の例では、1 回のコールで取引先責任者のリストを挿入することにより、取引先責任者を一括挿入します。次に、その取引先責任者を一括更新します。
- 開発者コンソールで匿名 Apex を使用して次のスニペットを実行します。
// Create a list of contacts List<Contact> conList = new List<Contact> { new Contact(FirstName='Joe',LastName='Smith',Department='Finance'), new Contact(FirstName='Kathy',LastName='Smith',Department='Technology'), new Contact(FirstName='Caroline',LastName='Roth',Department='Finance'), new Contact(FirstName='Kim',LastName='Shain',Department='Education')}; // Bulk insert all contacts with one DML call insert conList; // List to hold the new contacts to update List<Contact> listToUpdate = new List<Contact>(); // Iterate through the list and add a title only // if the department is Finance for(Contact con : conList) { if (con.Department == 'Finance') { con.Title = 'Financial analyst'; // Add updated contact sObject to the list. listToUpdate.add(con); } } // Bulk update all contacts with one DML call update listToUpdate;
- 組織で最近作成された取引先責任者を調べます。
Finance (財務) 部門に所属する 2 人の取引先責任者の役職にはFinancial analyst
(財務アナリスト) と入力されています。
レコードを更新/挿入する
新規レコードと既存レコードが混在するリストの場合、upsert
ステートメントを使用して、リストの全レコードを対象とする挿入と更新を処理できます。upsert により、重複レコードが作成されるのを回避でき、最初に存在していたレコードを判別する必要がなくなるため、時間を節約できます。
upsert
ステートメントは、1 つの項目の値を比較して sObject と既存のレコードを照合します。このステートメントをコールするときに項目を指定しないと、upsert
ステートメントは sObject の ID を使用して sObject と Salesforce の既存のレコードを照合します。または、照合に使用する項目を指定できます。カスタムオブジェクトの場合、外部 ID とマークされたカスタム項目を指定します。標準オブジェクトの場合、idLookup プロパティが true に設定されている項目であれば指定できます。たとえば、取引先責任者またはユーザーのメール項目の idLookup プロパティは設定されています。項目のプロパティをチェックするには、『Object Reference for Salesforce and Lightning Platform (Salesforce および Lightning Platform のオブジェクトリファレンス)』を参照してください。
upsert の構文
upsert sObject | sObject[] upsert sObject | sObject[] field
省略可能な項目は、項目トークンです。たとえば、MyExternalID 項目を指定する場合のステートメントは次のようになります。
upsert sObjectList Account.Fields.MyExternalId;
upsert では、新規レコードを作成するか既存のレコードを更新するかを判別するために、sObject レコードの主キー (ID)、idLookup 項目、または外部 ID 項目を使用します。
- キーが一致しない場合、新規オブジェクトレコードが作成されます。
- キーが一度だけ一致したら、既存のオブジェクトレコードが更新されます。
- キーが複数回一致する場合は、エラーが生成され、オブジェクトレコードは挿入も更新もされません。
次の例は、1 回のコールで upsert が既存の取引先責任者レコードを更新し、新規取引先責任者を挿入する方法を示します。この upsert コールにより、既存の取引先責任者 Josh が更新され、新規取引先責任者 Kathy が挿入されます。
- 開発者コンソールの [Execute Anonymous (匿名実行)] ウィンドウで、次のスニペットを実行します。
// Insert the Josh contact Contact josh = new Contact(FirstName='Josh',LastName='Kaplan',Department='Finance'); insert josh; // Josh's record has been inserted // so the variable josh has now an ID // which will be used to match the records by upsert josh.Description = 'Josh\'s record has been updated by the upsert operation.'; // Create the Kathy contact, but don't persist it in the database Contact kathy = new Contact(FirstName='Kathy',LastName='Brown',Department='Technology'); // List to hold the new contacts to upsert List<Contact> contacts = new List<Contact> { josh, kathy }; // Call upsert upsert contacts; // Result: Josh is updated and Kathy is created.
- 組織のすべての取引先責任者を調べます。
組織の Josh Kaplan のレコードは 1 件のみになります (2 件ではありません)。これは、upsert 操作で既存のレコードが見つかったため、そのレコードが更新され、新規取引先責任者レコードは作成されなかったためです。Kathy Brown の取引先責任者レコードも 1 件あります。
または、レコードの照合に使用する項目を指定できます。たとえば、この例では idLookup プロパティが設定されているため、取引先責任者のメール項目が使用されています。この例では、取引先責任者 Jane Smith が挿入され、2 件目の取引先責任者 sObject が作成されて、同じメールアドレスが入力されます。その後で、upsert
がコールされ、照合にメール項目を使用して取引先責任者が更新されます。
- 開発者コンソールの [Execute Anonymous (匿名実行)] ウィンドウで、次のスニペットを実行します。
Contact jane = new Contact(FirstName='Jane', LastName='Smith', Email='jane.smith@example.com', Description='Contact of the day'); insert jane; // 1. Upsert using an idLookup field // Create a second sObject variable. // This variable doesn’t have any ID set. Contact jane2 = new Contact(FirstName='Jane', LastName='Smith', Email='jane.smith@example.com', Description='Prefers to be contacted by email.'); // Upsert the contact by using the idLookup field for matching. upsert jane2 Contact.fields.Email; // Verify that the contact has been updated System.assertEquals('Prefers to be contacted by email.', [SELECT Description FROM Contact WHERE Id=:jane.Id].Description);
- 組織のすべての取引先責任者を調べます。
組織の取引先責任者 Jane Smith は 1 件のみで、説明が更新されています。
レコードを削除する
delete
ステートメントを使用して保持されているレコードを削除できます。レコードを削除しても Lightning プラットフォームから完全に削除されるわけではなく、復元できるように 15 日間はごみ箱に置かれます。
次の例は、姓が Smith の取引先責任者をすべて削除する方法を示します。一括 DML のサンプルを実行済みの場合、組織にはすでに姓が Smith の取引先責任者が 2 件あります。開発者コンソールで [Anonymous Apex (匿名 Apex)] を使用して次のスニペットを実行し、姓が Smith の取引先責任者がなくなったことを確認します。
Contact[] contactsDel = [SELECT Id FROM Contact WHERE LastName='Smith']; delete contactsDel;
DML ステートメントの例外
DML 操作が失敗した場合、DmlException
型の例外が返されます。コードで例外をキャッチして、エラー状況を処理できます。
次の例では、必須の名前項目なしで取引先の挿入を試みたため、DmlException
が生成されます。例外は、catch ブロックでキャッチされます。
try { // This causes an exception because // the required Name field is not provided. Account acct = new Account(); // Insert the account insert acct; } catch (DmlException e) { System.debug('A DML exception has occurred: ' + e.getMessage()); }
データベースメソッド
Apex には、組み込みの Database クラスが含まれており、DML 操作を実行し、対応する DML ステートメントと同じ動作をするメソッドを提供します。
次のデータベースメソッドは静的で、クラス名でコールされます。
Database.insert()
Database.update()
Database.upsert()
Database.delete()
Database.undelete()
Database.merge()
DML ステートメントとは異なり、データベースメソッドには省略可能な allOrNone パラメーターがあり、操作を部分的に完了させる必要があるかどうかを指定できます。このパラメーターを false
に設定すると、一部のレコードでエラーが発生した場合、成功したレコードはコミットされ、失敗したレコードについてはエラーが返されます。また、部分的な完了オプションでは、例外を発生させません。
次の例は、allOrNone を false
に設定して insert
メソッドをコールする方法を示します。
Database.insert(recordList, false);
データベースメソッドは、各レコードの成功または失敗情報が含まれる結果オブジェクトを返します。たとえば、挿入操作と更新操作はそれぞれ Database.SaveResult
オブジェクトの配列を返します。
Database.SaveResult[] results = Database.insert(recordList, false);
デフォルトでは、allOrNone パラメーターは true
に設定されているため、データベースメソッドは対応する DML ステートメントと同様に動作し、エラーの場合は例外を発生させます。
次の 2 つのステートメントは insert recordList;
ステートメントと同等です。
Database.insert(recordList);
と
Database.insert(recordList, true);
例: 部分的な完了を指定してレコードを挿入する
データベースメソッドを使用する次の例を見てみましょう。この例は、一括 DML の例に基づいていますが、DML ステートメントがデータベースメソッドに置き換えられています。Database.insert()
メソッドは、部分的な完了オプションを指定してコールされます。リストの 1 つの取引先責任者には意図的に一切の項目がありません。取引先責任者は必須の LastName 項目がないと保存できないため、これによりエラーが発生します。3 つの取引先責任者はコミットされ、項目のない取引先責任者ではエラーが発生します。この例の最後の部分では、返された結果を反復処理して、デバッグログにデバッグメッセージを書き出します。
- 開発者コンソールの [Execute Anonymous (匿名実行)] ウィンドウで、次の例を実行します。
// Create a list of contacts List<Contact> conList = new List<Contact> { new Contact(FirstName='Joe',LastName='Smith',Department='Finance'), new Contact(FirstName='Kathy',LastName='Smith',Department='Technology'), new Contact(FirstName='Caroline',LastName='Roth',Department='Finance'), new Contact()}; // Bulk insert all contacts with one DML call Database.SaveResult[] srList = Database.insert(conList, false); // Iterate through each returned result for (Database.SaveResult sr : srList) { if (sr.isSuccess()) { // Operation was successful, so get the ID of the record that was processed System.debug('Successfully inserted contact. Contact ID: ' + sr.getId()); } else { // Operation failed, so get all errors for(Database.Error err : sr.getErrors()) { System.debug('The following error has occurred.'); System.debug(err.getStatusCode() + ': ' + err.getMessage()); System.debug('Contact fields that affected this error: ' + err.getFields()); } } }
- デバッグメッセージを確認します (検索条件に DEBUG キーワードを使用)。
1 つのエラーがレポートされ、3 つの取引先責任者は挿入されています。
DML ステートメントとデータベースメソッドの使い分け
- DML 一括処理中に発生するエラーを、コントロールフローをその場で中断する Apex 例外として処理する場合、DML ステートメントを使用します。ここでは
try...catch
ブロックを使用します。この動作は、ほとんどのデータベース手続き型言語での例外の処理方法に似ています。
- DML 一括操作の部分的な完了を可能にする場合は、Database クラスメソッドを使用します。レコードが失敗した場合でも、DML 操作の残りは終了できます。アプリケーションは拒否されたレコードを確認でき、可能であれば操作を再試行します。この形式を使用すると、DML 例外エラーが発生することがないコードを書くことができます。エラーが発生しない代わりに、作成したコードでは、成功または失敗を判断するための適切な結果配列を使用できます。Database クラスメソッドには、DML ステートメントに類似する、発生した例外をサポートする構文も含まれます。
関連レコードを操作する
リレーションによって互いに関連するレコードを作成し、管理します。
関連レコードを挿入する
2 つのオブジェクト間のリレーション (参照関係や主従関係など) がすでに定義されている場合、既存のレコードに関連するレコードを挿入できます。レコードは、外部キー ID を使用して関連レコードに関連付けられます。たとえば、新規取引先責任者を挿入する場合、AccountId
項目の値を設定することで、取引先責任者の関連取引先レコードを指定できます。
この例では、取引先責任者の AccountId
項目を設定して、取引先責任者を取引先 (関連レコード) に追加する方法を示します。取引先責任者と取引先は参照関係でリンクされています。
- 開発者コンソールの [Anonymous Apex (匿名 Apex)] ウィンドウで次のスニペットを実行します。
Account acct = new Account(Name='SFDC Account'); insert acct; // Once the account is inserted, the sObject will be // populated with an ID. // Get this ID. ID acctID = acct.ID; // Add a contact to this account. Contact mario = new Contact( FirstName='Mario', LastName='Ruiz', Phone='415.555.1212', AccountId=acctID); insert mario;
- 組織の取引先責任者を調べます。
新規取引先 (SFDC Account) が作成されており、取引先の取引先責任者関連リストに取引先責任者 Mario Ruiz が含まれています。
関連レコードを更新する
関連レコードの項目は、同じ DML 操作のコールでは更新できないため、別の DML コールが必要になります。たとえば、新規取引先責任者を挿入する場合、AccountId
項目の値を設定することで、取引先責任者の関連取引先レコードを指定できます。ただし、別の DML コールを使用して取引先自体を更新しない場合、取引先の名前を変更することはできません。同様に、取引先責任者を更新するときに、取引先責任者の関連取引先も更新する場合は、2 つの DML コールを作成する必要があります。次の例では、2 つの update
ステートメントを使用して取引先責任者とその関連取引先を更新しています。
// Query for the contact, which has been associated with an account. Contact queriedContact = [SELECT Account.Name FROM Contact WHERE FirstName = 'Mario' AND LastName='Ruiz' LIMIT 1]; // Update the contact's phone number queriedContact.Phone = '(415)555-1213'; // Update the related account industry queriedContact.Account.Industry = 'Technology'; // Make two separate calls // 1. This call is to update the contact's phone. update queriedContact; // 2. This call is to update the related account's Industry field. update queriedContact.Account;
関連レコードを削除する
delete
操作では、カスケード削除がサポートされています。親オブジェクトを削除すると、各子レコードが削除可能な場合は自動的に削除されます。
たとえば、前に作成した取引先 (SFDC Account) を削除すると、その関連取引先責任者も削除されます。
- 開発者コンソールの [Anonymous Apex (匿名 Apex)] ウィンドウで次のスニペットを実行します。
Account[] queriedAccounts = [SELECT Id FROM Account WHERE Name='SFDC Account']; delete queriedAccounts;
- 組織の取引先と取引先責任者を調べます。
取引先とその関連取引先責任者の両方が削除されています。
トランザクションについて
DML 操作はトランザクション内で実行されます。トランザクションの実行には、すべての DML 操作が正常に完了することが求められます。いずれかの操作でエラーが発生した場合はトランザクション全体がロールバックされます。この場合、データは一切データベースにコミットされません。トランザクションの境界は、トリガー、クラスメソッド、匿名のコードブロック、Apex ページ、カスタム Web サービスメソッドのいずれかにすることができます。たとえば、トリガーまたはクラスが 2 つの取引先を作成し、1 つの取引先責任者を更新した場合、入力規則エラーでその取引先責任者の更新が失敗すると、トランザクション全体がロールバックされて、Salesforce にはいずれの取引先も保持されません。
リソース
- Apex 開発者ガイド
- Apex 開発者ガイド: Apex でのデータの操作
- Apex 開発者ガイド: DML を使用したデータの追加および取得
- Apex 開発者ガイド: Database クラス
- Apex 開発者ガイド: Exception のステートメント