[Salesforce] エラーが出てもエラーレコード以外は更新させる
salesforceでインサート・アップデートする場合、ガバナ制限の関係から都度DMLを発行せずにある程度まとめてやる事が多い。
その際に、一件でもエラーが出てしまうと他もまきこまれてロールバックされてしまうので、エラー以外は更新させる方法。
通常の方法
通常の方法、というか、簡易な方法としては、以下のようにインサートする。
1 2 3 4 5 |
|
for
文の中でinsert
せずに、リストにまとめておいて外で一回だけinsert
する。
これで、DMLは一回だけ発行したことになるので、節約が可能。
ループの中ではSOQL、DMLはしない。これがapexの鉄則。
しかしこれだと例えば全部で200件インサートする予定があって、そのうち150件目に不備がありエラーが出た場合、 そこで処理がストップし、それまでにインサートした149件もすべてロールバックされインサート自体がなかったことになる。 要件によってはこの方がいい場合もあるが、エラーが出たレコード以外は全てインサートしたい場合もある。
Database Class
そんな時は、Database
クラスを使う。
使い方は、Database.insert()
とするだけ。
このメソッドの第1引数に、インサートするオブジェクトのリストを、 第2引数に、エラー時の挙動を入れる。
- trueの場合はエラー時には全てロールバック(上記の単純な
insert
と同じ)(デフォルト) - falseの場合はエラーレコード以外はインサートする
上記の例を書き換えると、以下のようになる。
1 2 3 4 5 |
|
これでエラーのレコード以外はインサートさせる事が出来た。
updata
、upsert
、delete
についても同じ。
Upsert
upsertも上記と方法は同じだが、upsertでは外部IDの指定が可能になるが、その方法が少し簡易バージョンとは違った。
例えば、Lead
オブジェクトの、カスタム項目、UserID__c
を外部IDとして指定する場合。
簡易バージョンの場合は以下のようになる。
1 2 3 |
|
上記、Database
を使ったパターンだと、ドキュメントには以下のように書かれている。
1
|
|
この第二引数のexternalIdField
が外部IDの指定箇所になるが、型は、Schema.SObjectField
となっていて、
そのまま Database.upsert(lists, UserID__c, false)
とやるとエラーが出る。
なので以下のようにして指定したい項目のSchema.SObjectField
を取得して指定してやる必要がある。
1 2 3 4 5 |
|
これで外部IDを指定したアップサートが可能となる。
エラーハンドリング
さて、DMLでエラーが発生した場合、その内容を取得するには、try..catch
で例外を拾っていた。
1 2 3 4 5 6 |
|
しかし、Database
を使ってinsertなどをした場合は別の方法で取得する。
というか、エラーだけでなく、結果を一件ずつ取得することが出来るため、それらからエラー分を取得する、という形になる。
SaveResult
クラスを使用する。
1
|
|
取得した結果から、成功、失敗を取得し、内容を取得したりする。
1 2 3 4 5 6 7 8 9 10 11 12 |
|
例えば出力内容は以下のような感じ。
1 2 |
|
この SaveResult
は、インサート時の結果を取得するためのクラスで、
インサート、アップサート、アップサート、デリート、それぞれのクラスが別々に用意されている。
- insert – SaveResult
- update – UndeleteResult
- upsert – UpsertResult
- delete – DeleteResult
詳しくは、Apex 開発者ガイド、を参照。
(直接のリンクがなぜか貼れなかった)
Apex開発者ガイド – リファレンス – Database名前空間
まとめ
以上で一通り想定していた動作をさせることができた。
エラー時に他をロールバックするかどうかとかは、結構忘れがちになるけれど、結構難しい問題。
仕様策定時にきちんと考慮していきたい。