こんにちは、アンダーソンです。
今回は上級デベロッパーの勉強中に気になったシリーズです。
DataBaseクラスをそこまでちゃんと使ったことがないので、
今回はSavePointとRollbackでどんなことができるのか実験もかねてやっていきたいと思います。
Contents
Databaseクラス
Databaseクラスはかなりたくさんのメソッドを持っています。
リードのコンバートだったり、動的SOQLの実行、DMLの実行など多岐に渡ります。
今回はその中のsetSavepoint()とrollback(databaseSavepoint)メソッドについて実験していきます。
実際にやることとしては、どんな時にエラーが吐かれるかを実験していきます。
一応それぞれの内容を引用したものを載せておきます。
ローカル変数として保存でき、rollback メソッドで使用してデータベースをその時点に復元できる savepoint 変数を返します。
setSavepoint()
データベースを、savepoint 変数で指定された状態に復元します。最後の savepoint 後に送信されたメールもロールバックされ、送信されません。
rollback(databaseSavepoint)
SavePointを変数として保存しておき、その変数の状態にロールバックできるメソッドということになります。
今回やったこと
①まずは普通にやってみる
Contact con = new Contact(LastName = 'テスト',LeadSource = 'Web'); insert con; system.debug('インサート直後のリードソース=' + [SELECT Id,LeadSource FROM Contact Where Id = :con.Id].LeadSource); SavePoint sp = Database.setSavepoint(); system.debug('セーブ後のリードソース=' + [SELECT Id,LeadSource FROM Contact Where Id = :con.Id].LeadSource); con.LeadSource = 'Email'; update con; system.debug('アップデート後のリードソース=' + [SELECT Id,LeadSource FROM Contact Where Id = :con.Id].LeadSource); Database.rollback(sp); system.debug('ロールバック後のリードソース=' + [SELECT Id,LeadSource FROM Contact Where Id = :con.Id].LeadSource);
4箇所で値の遷移を確認してみました。
レコードの挿入→セーブ→レコードの更新→ロールバックの順にやってます。
下記が実行結果です。

ちゃんといけてますね。
想定通り、ロールバック後にはWebに戻っています。
そして注目なのがIdは変わっていないということです。
②ロールバックされたレコードをインサートしてみる
Contact con = new Contact(LastName = 'テスト',LeadSource = 'Web'); insert con; system.debug('インサート直後のリードソース=' + [SELECT Id,LeadSource FROM Contact Where Id = :con.Id]); SavePoint sp = Database.setSavepoint(); system.debug('セーブ後のリードソース=' + [SELECT Id,LeadSource FROM Contact Where Id = :con.Id]); con.LeadSource = 'Email'; update con; system.debug('アップデート後のリードソース=' + [SELECT Id,LeadSource FROM Contact Where Id = :con.Id]); Database.rollback(sp); system.debug('ロールバック後のリードソース=' + [SELECT Id,LeadSource FROM Contact Where Id = :con.Id]); con.LeadSource = 'Email'; //どうしてもEmailに変えたいようです。 insert con;
上記のコードを実行してみました。
結果はエラーが返ってきます。

Idがすでにふられているものに対してinsertはできないため弾かれます。
③セーブポイントを複数指定してみる
次は二箇所にセーブポイントを刻んでみます。
レコード挿入後に1回、更新後に1回です。
それぞれ変数名は変更しています。
Contact con = new Contact(LastName = 'テスト',LeadSource = 'Web'); insert con; SavePoint sp1= Database.setSavepoint(); con.LeadSource = 'Email'; update con; SavePoint sp2 = Database.setSavepoint(); Database.rollback(sp1); system.debug('sp1=' + [SELECT Id,LeadSource FROM Contact WHERE Id = :con.Id]); Database.rollback(sp2); system.debug('sp2=' + [SELECT Id,LeadSource FROM Contact WHERE Id = :con.Id]);
これを実行すると、これまたエラーが出ます。

このコンテキストにはセーブポイントが存在しません。だそうです。
リファレンスを参照したところ、複数のセーブポイントを設置はできるものの、どれか一つにロールバックした時点で
そのセーブはなくなるということだそうです。
上記コードの場合、sp1にロールバックした時点でsp2のセーブは消えてしまったと考えるのが妥当でしょうか。
と思いきや逆を実行すると行けるんです。
④ロールバックは順序も大事
Contact con = new Contact(LastName = 'テスト',LeadSource = 'Web'); insert con; SavePoint sp1= Database.setSavepoint(); con.LeadSource = 'Email'; update con; SavePoint sp2 = Database.setSavepoint(); Database.rollback(sp2); system.debug('sp2=' + [SELECT Id,LeadSource FROM Contact WHERE Id = :con.Id]); Database.rollback(sp1); system.debug('sp1=' + [SELECT Id,LeadSource FROM Contact WHERE Id = :con.Id]);
先ほどとはロールバックの順序を変更してみました。
結果は以下の通りです。

ちゃんと出てます。あくまでも、最新のセーブにロールバックせずに古いセーブにロールバックすると
最新セーブは失われるということみたいです。
細かいですが覚えておいた方が良さそうですね。
ほかにもTriggerからの呼び出しなど制約はありそうですが、一旦基本の理解はこんなもんでいいのかなと思います。
うまく使って組織のデータの損失を避けられるような設計をしていきたいものですね。
コメント