之前对于Salesforce的RollBack机制没有进行深入的思考。
那天,闲来无事,有人问了我一个问题。
问说,下面的代码在匿名快执行完之后插进去了几条数据。
ObjectA__c objA = new ObjectA__c(); objA.Name = "TestA"; insert objA; List<ObjectB__c> objBList = new List<ObjectB__c> for(Integer i = 0; i < 200; i++) { ObjectB__c objB = new ObjectB__c(); objB.Name = "TestB" + i; insert ojbB; }
我大概扫了一眼,问的是Governor Limit嘛,答案是150,送分题,不谢。
但又隐约的觉得不大对劲,既然拿来问我,肯定有坑。
立马打开Developer Console试了一下。
看到了结果倒吸了一口冷气。。。。。。。。0,一条都没插进去。全都RollBack了?
咦?不对啊,那一定是匿名块做了特殊的设置。试试Controller。
要不然我以前在Controller里写个什么劲儿的SavePoint。
VF:
<apex:page controller="TestRollbackClass"> <apex:form> <apex:commandButton action="{!save}" value="Button"/> </apex:form> </apex:page>
Apex Class:
public class TestRollbackClass { public void save() { ObjectA__c oa = new ObjectA__c(); oa.Name = 'TestA'; insert oa; ObjectB__c ob = new ObjectB__c(); ob.Name = 'testB'; insert ob; } }
我在ObjectB上加了一个Validation Rule,保证新建ObjectB记录的一定会报错。
这次总该如预期一般只有ObjectA被插进去了吧。
结果一跑,唉? 还是都RollBack了?
瞬间怀疑人生。既然都自动RollBack了,那我以前为什么必须写SavePoint?
去翻了下Release Note,也没找到相关的变动。
没办法,一定是写法问题,我试着按照Coding Standard写一次
public class TestRollbackClass { public void save() { ObjectA__c oa = new ObjectA__c(); oa.Name = 'Test2'; insert oa; try { ObjectB__c ob = new ObjectB__c(); ob.Name = 'test1'; insert ob; } catch(Exception e) { ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.Error, e.getMessage())); } } }
再次执行,第一条数据ObjectA居然插进去了。
我瞬间明白了点什么。
Salesforce的机制应该是这样的,
如果Transaction被异常中断,则Rollback全部的数据变动。
如果异常被捕获,则已经正常commit的数据变动会落实。出错的部分无效。
按照开发潜规则,应该展现给User更Friendly的错误信息,而不是可怕的Trace Log信息。
所以我以前的公司要求捕获所有可能的异常并替换成友好的错误信息。
这样的话,Transaction一定不会被异常中断,所以自动全部Rollback的机制从来没有启用过。
那么为了防止在出错时插入脏数据,Salesforce提供了SavePoint。
用法如下:
public class TestRollbackClass { public void save() { SavePoint sp = Database.setSavepoint(); ObjectA__c oa = new ObjectA__c(); oa.Name = 'Test2'; insert oa; try { ObjectB__c ob = new ObjectB__c(); ob.Name = 'test1'; insert ob; } catch(Exception e) { Database.rollback(sp); ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.Error, e.getMessage())); } } }
如果ObjectB插入出错,已insert的ObjectA也会回滚。达到目的。