Apex のトランザクション
トランザクション境界内部で発生するすべての操作は、操作の 1 つの単位に相当します。これは、トランザクション境界内で実行されたコードの結果として起動されたクラスやトリガなど、トランザクション境界から外部コードへのコールにも適用されます。たとえば、カスタム Apex Web サービスメソッドによってトリガが起動し、そのトリガがクラスのメソッドをコールするという連続した操作があるとします。この場合、トランザクション内のすべての操作がエラーなしで実行を完了した後にのみ、すべての変更がデータベースにコミットされます。中間ステップのいずれかでエラーが発生した場合、すべてのデータベース変更はロールバックされ、トランザクションはコミットされません。
トランザクションが便利な場合とは?
トランザクションは、複数の操作が関連していて、それらの操作のすべてをコミットするか、一切コミットしないかのいずれかにする必要がある場合に便利です。これにより、データベースは整合性の取れた状態に保たれます。トランザクション処理は、さまざまなビジネスシナリオで活用されています。たとえば、一般的なシナリオとして銀行口座間での送金があります。最初の口座から送金額を引き落とし、その金額を 2 つ目の口座に入金します。これら 2 つの操作は、一緒にデータベースにコミットする必要があります。引き落とし操作が成功して入金操作が失敗したような場合に、口座残高に矛盾が生じることを防ぐためです。
例
この例は、最後の操作で入力規則エラーが発生した場合、メソッドのすべての DML insert 操作がどのようにロールバックされるかを示しています。この例では、invoice メソッドがトランザクション境界です。つまり、このメソッド内で実行されるすべてのコードは、プラットフォームデータベースにすべての変更をコミットするか、すべての変更をロールバックします。この場合、Line Item (品目名) として鉛筆を指定した、新しい請求書明細を追加します。この Line Item を使用して、5,000 本の鉛筆を購入 (Units_Sold__c 項目に指定) します。これは鉛筆の全在庫数量 1,000 本を上回ります。この例では、入力規則が商品品目の全在庫数量が新規購入に足りるかどうかをチェックするように設定されていることが前提となります。
この例では、在庫数量 (1,000) よりも多く (5,000) の鉛筆を購入しようとしているので、入力規則は失敗して、例外が発生します。コードの実行はこの時点で停止し、この例外よりも前に処理されたすべての DML 操作はロールバックされます。この場合、請求書明細と品目名はデータベースには追加されず、その insert DML 操作はロールバックされます。
1// Only 1,000 pencils are in stock.
2// Purchasing 5,000 pencils cause the validation rule to fail,
3// which results in an exception in the invoice method.
4Id invoice = MerchandiseOperations.invoice('Pencils', 5000, 'test 1');これは invoice メソッドの定義です。この場合、全在庫数量を更新すると、入力規則のエラーにより例外が発生します。その結果、請求書明細と品目はロールバックされ、データベースには挿入されません。
1public class MerchandiseOperations {
2 public static Id invoice( String pName, Integer pSold, String pDesc) {
3 // Retrieve the pencils sample merchandise
4 Merchandise__c m = [SELECT Price__c,Total_Inventory__c
5 FROM Merchandise__c WHERE Name = :pName LIMIT 1];
6 // break if no merchandise is found
7 System.assertNotEquals(null, m);
8 // Add a new invoice
9 Invoice_Statement__c i = new Invoice_Statement__c(
10 Description__c = pDesc);
11 insert i;
12
13 // Add a new line item to the invoice
14 Line_Item__c li = new Line_Item__c(
15 Name = '1',
16 Invoice_Statement__c = i.Id,
17 Merchandise__c = m.Id,
18 Unit_Price__c = m.Price__c,
19 Units_Sold__c = pSold);
20 insert li;
21
22 // Update the inventory of the merchandise item
23 m.Total_Inventory__c -= pSold;
24 // This causes an exception due to the validation rule
25 // if there is not enough inventory.
26 update m;
27 return i.Id;
28 }
29}