セーブポイント考察

SavePointの複雑なケースについての動作確認。

スポンサーリンク

セーブポイントの使い方応用

SavePointはDBの状態を保存しておくための手段。
複数オブジェクトに対するデータ操作に置いて、一部エラーだった時の原子性を担保するために使用されます。
業務で使うことはあっても、上級試験となると特殊でちょっと意地悪なケースも問われるので、動きを確認しておきましょう。

ケース1

以下のコードで動作を確認。

Account a = new Account(Name='hoge');
Savepoint sp1 = Database.setSavepoint(); // ①
insert a;  // ②
Database.rollback(sp1);  // ③
Savepoint sp2 = Database.setSavepoint();
a.Name = 'hoge1';
insert a; // ④ ここでエラー
delete a;

このコードを実行すると、④のところで以下のエラーが発生します。

System.DmlException: Insert failed. First exception on row 0 with id 0011000001Lc5PEAAZ; first error: INVALID_FIELD_FOR_INSERT_UPDATE, cannot specify Id in an insert call: [Id]

なぜ、こういうエラーが出たかということを、順次追っていくと、まず、①の時点でDBが空の状態のセーブポイントが作成されます。

Insert前のデータベース状態

次に②でInsertした時に、レコードへの登録と同時にIDが割り当てられ、変数aにも同時にIDがセットされます。
Insert後のデータベース状態

その後、③でロールバックする訳ですが、ここで重要なのが、ロールバックはDBの状態をセーブポイント時点のものに戻すものであってApexの変数は元に戻らない(そのまま保持される)ということに注意してください。
つまり、変数aはIDが割り当てられた状態をキープしています。
ロールバック後のデータベース状態

そして、④でのInsertで、IDが付いているレコードに対してInsertをかけようとしているのでエラーとなります。
再Insert時にIDが設定されているためエラーになる

Insertなのに、IDが付いているのが問題なので、InsertをUpsertにした場合は処理が成功します。

ケース2

以下のコードで動作を確認。

Account a = new Account(Name='hoge');
Savepoint sp1 = Database.setSavepoint();  // ①
insert a;
Savepoint sp2 = Database.setSavepoint();  // ②
Database.rollback(sp1);  // ③
Database.rollback(sp2);  // ④ ここでエラー

このコードでは④でエラーが発生します。

System.TypeException: Savepoint does not exist in this context

ここでのApexとDBの状態を図解するとこんな感じになります。
まず、①でセーブポイントが作成されます。
Insert前のデータベース状態(セーブポイント1)

次に②でセーブポイントが作成されます。
Insert後のデータベース状態(セーブポイント2)

そして、③でsp1にロールバックします。
この時、DB側だけ時間旅行することになり、sp1の後に作成されたセーブポイントもなかったことになります
セーブポイント1でロールバック後のデータベース状態

最後、④でsp1より後に作成されたsp2へロールバックすると、なかったことになっているsp2は当然存在しないため、エラーとなります。
タイムマシンでいうと、過去へ戻ることはできて、未来へ進めることはできません。
セーブポイント2は未設定状態なのでロールバックできない

ここでは、セーブポイントは時系列が考慮されるというのがポイントです。
ロールバックをsp2→sp1の順番にすれば、sp2の時点でsp1のセーブポイントが作成されているので、こちらは成功します。

まとめ

色々書きましたが、覚えておくポイントは2点。
これが頭に入っていれば、試験で問われる応用ケースにも対応できると思います。

  • SavePointはDBの状態が記録され、変数の状態までは保持されない
  • セーブポイントにロールバックした時、対象セーブポイント以降に作成したセーブポイントはロールバックできない(時系列が考慮される)

コメント