Memos About Salesforce

Salesforceにハマってたこと!

salesforce savepoint セーブポイント ロールバック トランザクションの制御

f:id:jude2016:20190920155743j:plain
programming

こんにちは、管理人の@Salesforce.Zです。

今日は、コーディングに欠けない実装考えをひとつ共有します。

ベテランなら、きっと詳しいだが、素人や初心者はあまり知らないかもしれません。

読んだら得ること

★ ロールバックロジック

目次

ロールバック

rollback

コンピュータ用語では、データ更新・修正などで障害が起こったときに、その前の状態まで戻る処理のこと

なぜロールバックが必要

銀行の振込処理を例にいうと

AからBに3万円を振込する予定、お金がAさんの口座からBさんの口座に完全に移動できるまでのことをトランザクションとする

1.AさんがBさんの口座情報を振込フォームに入れます(口座番号や金額、あるいは存在しない番号など)

2.Bさんの口座情報を考え込んで、正しいと、確認をし、最後に振込確定まで行きました。

3.しかし、口座情報が間違えて、存在しない口座番号の場合、送金してしまうと、お金が移動してしますね。こんな時にロールバックで、送金しようとする前のAさんの口座状態に戻らないといけない

例がよくないかもしれないが、こんな感じで、ミスや失敗などの時にやったことを全部白紙にする感じ

どうロールバックを実装する

Salesforceの取引先に対して、データを操作する例にします。

//取引先データを登録する(まだデータベースにコミットしていません)
Account a = new Account(Name = 'xxx');
insert a;
System.assertEquals(null, [SELECT AccountNumber FROM Account WHERE Id = :a.Id].
                           AccountNumber);

// セーブポイントを用意する(windowsのバックアップみたい)
Savepoint sp = Database.setSavepoint();

// 取引先番号(AccountNumber)の値を変更します
a.AccountNumber = '123';
// 取引先を更新します
update a;

// 取引先番号が上記の設定値ではない場合
if([SELECT AccountNumber FROM Account WHERE Id = :a.Id].AccountNumber != '123'){
    // 用意したセーブポイントのバックアップの状態に戻ります
    Database.rollback(sp);
 // 戻った後に取引先番号がセーブポイント時点では、nullのはず、nullではない場合エラーになります
 System.assertEquals(null, [SELECT AccountNumber FROM Account WHERE Id = :a.Id].AccountNumber);
}

ロールバックの注意点

次の制限事項は、savepoint 変数の生成とデータベースのロールバックに適用されます。

・複数の savepoint を設定し、生成した最新 savepoint ではない savepoint にロールバックすると、ロールバックされた savepoint 変数は無効になります。たとえば、最初に savepoint SP1 を生成し、次に savepoint SP2 を生成した場合、SP1 にロールバックすると、変数 SP2 は無効になります。その変数を使用しようとすると、ランタイムエラーが発生します。

・各トリガ呼び出しが新しいトリガコンテキストであるため、savepoints への参照は、トリガ呼び出しを通過することはできません。静的変数として savepoint を宣言し、トリガコンテキスト全体で使用しようとすると、ランタイムエラーが発生します。

・設定した各セーブポイントは、DML ステートメントのガバナ制限にカウントされます。 ロールバック中、静的変数は戻されません。トリガの実行を再試行する場合、静的変数には最初の実行から得た値が維持されます。

・各ロールバックは、DML ステートメントのガバナ制限にカウントされます。データベースをそれ以上の回数ロールバックしようとすると、ランタイムエラーが発生します。

・savepoint の設定後に挿入された sObject の ID は、ロールバック後にクリアされません。ロールバック後に挿入するには、sObject を作成します。ロールバック前に作成した変数を使用して sObject を挿入しようとすると、その sObject 変数には ID があるため失敗します。同じ変数を使用して sObject を更新または更新/挿入しようとした場合も、sObject はデータベース内に存在せず、更新できないため失敗します。

最後に

普段のコーディングでは、入れるべき、ないと依頼元に怒られそう(笑)

設計書レベルではなく、コーダーの常識っす。