Skip to main content

DML を使用してレコードを操作する

学習の目的

この単元を完了すると、次のことができるようになります。
  • DML を使用してレコードを挿入、更新、削除する。
  • DML ステートメントを一括実行する。
  • upsert を使用してレコードを挿入または更新する。
  • DML 例外をキャッチする。
  • データベースメソッドを使用して部分的な完了オプションを指定し、新規レコードを挿入して結果を処理する。
  • DML ステートメントを使用する場合とデータベースメソッドを使用する場合を理解する。
  • 関連レコードに対して DML 操作を実行する。
メモ

メモ

日本語で受講されている方へ
このバッジの Trailhead ハンズオン Challenge は英語以外の言語には対応していません。Trailhead Playground の [言語] を [English] に、[地域] を [アメリカ合衆国] に切り替えてください。こちらの指示に従ってください。

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 コール後、sObject 変数に ID が含まれているため、システムが ID を照合してこの sObject 変数を対応するレコードに一致させられるようになり、sObject 変数を再利用してさらに更新などの DML 操作を実行できます。

データベースからレコードを取得してその項目 (ID 項目など) を取得できますが、この操作は DML では実行できません。SOQL を使用してクエリを作成する必要があります。SOQL については、別の単元で説明します。

一括 DML

DML 操作は、単一の sObject で行うことも、sObject のリストで一括で行うこともできます。一括 DML 操作ではガバナ制限 (Apex トランザクションごとのステートメント数を 150 件に制限する DML 制限など) に達することを防止できるため、この操作を実行することをお勧めします。この制限は、Lightning Platform の共有リソースに公正にアクセスできるようにするために設定されています。sObject のリストで DML 操作を実行すると、sObject ごとに 1 つのステートメントとみなされるのではなく、1 つの DML ステートメントとみなされます。

次の例では、1 回のコールで取引先責任者のリストを挿入することにより、取引先責任者を一括挿入します。次に、その取引先責任者を一括更新します。

  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;
  2. 組織で最近作成された取引先責任者を調べます。

    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 が挿入されます。

メモ

メモ

この upsert コールでは、ID を使用して最初の取引先責任者を照合します。変数 josh は、upsert コールに再利用されています。この変数にはすでに前の insert コールからレコード ID が入力されているため、この例では ID を明示的に設定する必要はありません。

  1. 開発者コンソールの [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.
  2. 組織のすべての取引先責任者を調べます。

    組織の Josh Kaplan のレコードは 1 件のみになります (2 件ではありません)。これは、upsert 操作で既存のレコードが見つかったため、そのレコードが更新され、新規取引先責任者レコードは作成されなかったためです。Kathy Brown の取引先責任者レコードも 1 件あります。

または、レコードの照合に使用する項目を指定できます。たとえば、この例では idLookup プロパティが設定されているため、取引先責任者のメール項目が使用されています。この例では、取引先責任者 Jane Smith が挿入され、2 件目の取引先責任者 sObject が作成されて、同じメールアドレスが入力されます。その後で、upsert がコールされ、照合にメール項目を使用して取引先責任者が更新されます。

メモ

メモ

この例で upsert ではなく insert が使用された場合、重複した取引先責任者 Jane Smith が挿入されます。

  1. 開発者コンソールの [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);
  2. 組織のすべての取引先責任者を調べます。

    組織の取引先責任者 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;
メモ

メモ

このスニペットには、取引先責任者を取得するクエリ (SOQL クエリ) が含まれます。SOQL の詳細は、別の単元で説明します。

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 に設定すると、一部のレコードでエラーが発生した場合、成功したレコードはコミットされ、失敗したレコードについてはエラーが返されます。また、部分的な完了オプションでは、例外を発生させません。

次の例は、allOrNonefalse に設定して insert メソッドをコールする方法を示します。

Database.insert(recordList, false);

データベースメソッドは、各レコードの成功または失敗情報が含まれる結果オブジェクトを返します。たとえば、挿入操作と更新操作はそれぞれ Database.SaveResult オブジェクトの配列を返します。

Database.SaveResult[] results = Database.insert(recordList, false);
メモ

メモ

更新/挿入では、Database.UpsertResult オブジェクト、削除では Database.DeleteResult オブジェクトが返されます。

デフォルトでは、allOrNone パラメーターは true に設定されているため、データベースメソッドは対応する DML ステートメントと同様に動作し、エラーの場合は例外を発生させます。

次の 2 つのステートメントは insert recordList; ステートメントと同等です。

Database.insert(recordList);

Database.insert(recordList, true);
メモ

高度な操作

これらのメソッドに加え、Database クラスには、DML ステートメントとして提供されないメソッドが含まれます。たとえば、トランザクションのコントロールとロールバックを行うメソッドやごみ箱を空にするために使用されるメソッド、SOQL クエリに関連するメソッドなどです。SOQL については、別の単元で説明します。

例: 部分的な完了を指定してレコードを挿入する

データベースメソッドを使用する次の例を見てみましょう。この例は、一括 DML の例に基づいていますが、DML ステートメントがデータベースメソッドに置き換えられています。Database.insert() メソッドは、部分的な完了オプションを指定してコールされます。リストの 1 つの取引先責任者には意図的に一切の項目がありません。取引先責任者は必須の LastName 項目がないと保存できないため、これによりエラーが発生します。3 つの取引先責任者はコミットされ、項目のない取引先責任者ではエラーが発生します。この例の最後の部分では、返された結果を反復処理して、デバッグログにデバッグメッセージを書き出します。

  1. 開発者コンソールの [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());
    	 }
        }
    }
  2. デバッグメッセージを確認します (検索条件に DEBUG キーワードを使用)。

    1 つのエラーがレポートされ、3 つの取引先責任者は挿入されています。

DML ステートメントとデータベースメソッドの使い分け

  • DML 一括処理中に発生するエラーを、コントロールフローをその場で中断する Apex 例外として処理する場合、DML ステートメントを使用します。ここでは try...catch ブロックを使用します。この動作は、ほとんどのデータベース手続き型言語での例外の処理方法に似ています。
  • DML 一括操作の部分的な完了を可能にする場合は、Database クラスメソッドを使用します。レコードが失敗した場合でも、DML 操作の残りは終了できます。アプリケーションは拒否されたレコードを確認でき、可能であれば操作を再試行します。この形式を使用すると、DML 例外エラーが発生することがないコードを書くことができます。エラーが発生しない代わりに、作成したコードでは、成功または失敗を判断するための適切な結果配列を使用できます。Database クラスメソッドには、DML ステートメントに類似する、発生した例外をサポートする構文も含まれます。

リソース

無料で学習を続けましょう!
続けるにはアカウントにサインアップしてください。
サインアップすると次のような機能が利用できるようになります。
  • 各自のキャリア目標に合わせてパーソナライズされたおすすめが表示される
  • ハンズオン Challenge やテストでスキルを練習できる
  • 進捗状況を追跡して上司と共有できる
  • メンターやキャリアチャンスと繋がることができる