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;
このコードを実行すると、④のところで以下のエラーが発生します。
なぜ、こういうエラーが出たかということを、順次追っていくと、まず、①の時点でDBが空の状態のセーブポイントが作成されます。
次に②でInsertした時に、レコードへの登録と同時にIDが割り当てられ、変数aにも同時にIDがセットされます。
その後、③でロールバックする訳ですが、ここで重要なのが、ロールバックはDBの状態をセーブポイント時点のものに戻すものであってApexの変数は元に戻らない(そのまま保持される)ということに注意してください。
つまり、変数aはIDが割り当てられた状態をキープしています。
そして、④でのInsertで、IDが付いているレコードに対してInsertをかけようとしているのでエラーとなります。
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); // ④ ここでエラー
このコードでは④でエラーが発生します。
ここでのApexとDBの状態を図解するとこんな感じになります。
まず、①でセーブポイントが作成されます。
次に②でセーブポイントが作成されます。
そして、③でsp1にロールバックします。
この時、DB側だけ時間旅行することになり、sp1の後に作成されたセーブポイントもなかったことになります。
最後、④でsp1より後に作成されたsp2へロールバックすると、なかったことになっているsp2は当然存在しないため、エラーとなります。
タイムマシンでいうと、過去へ戻ることはできて、未来へ進めることはできません。
ここでは、セーブポイントは時系列が考慮されるというのがポイントです。
ロールバックをsp2→sp1の順番にすれば、sp2の時点でsp1のセーブポイントが作成されているので、こちらは成功します。
まとめ
色々書きましたが、覚えておくポイントは2点。
これが頭に入っていれば、試験で問われる応用ケースにも対応できると思います。
- SavePointはDBの状態が記録され、変数の状態までは保持されない
- セーブポイントにロールバックした時、対象セーブポイント以降に作成したセーブポイントはロールバックできない(時系列が考慮される)
コメント