曾经作项目没有考虑那么多,对于级联表操做都是正常的一步一步操做,没有考虑过失败状况,最近项目碰见了失败的状况,致使碰到了相应的状况,特此mark一下,省得后期继续踩坑。数据库
需求以下:新建页面,页面中包含1.新建企业,2.新建联系人,3.新建机会。任何一步的逻辑或者DML操做失败都会致使总体的回滚。只有当三步都正常插入成功了之后才会跳转到新生成的机会的标准页面。spa
1.NewOpportunityController:这里作了一个逻辑判断,当联系人为空状况下,不容许新建联系人。固然,现实场景不会在这里判断,可是现实场景会有不少的复杂的业务逻辑,这里只是简单的处理。3d
1 public class newOpportunityController { 2 Account account; 3 Contact contact; 4 Opportunity opportunity; 5 OpportunityContactRole role; 6 7 public Account getAccount() { 8 if(account == null) 9 account = new Account(); 10 return account; 11 } 12 public Contact getContact() { 13 if(contact == null) 14 contact = new Contact(); 15 return contact; 16 } 17 public Opportunity getOpportunity() { 18 if(opportunity == null) 19 opportunity = new Opportunity(); 20 return opportunity; 21 } 22 public OpportunityContactRole getRole() { 23 if(role == null) 24 role = new OpportunityContactRole(); 25 return role; 26 } 27 28 29 public PageReference save() { 30 Savepoint sp = Database.setSavepoint(); 31 try { 32 account.phone = contact.phone; 33 insert account; 34 } catch(Exception e) { 35 Database.rollback(sp); 36 ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,'插入企业失败')); 37 38 return null; 39 } 40 try { 41 if(contact.phone == null) { 42 Database.rollback(sp); 43 ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,'联系人电话不能为空')); 44 return null; 45 } 46 contact.accountId = account.id; 47 insert contact; 48 } catch(Exception e) { 49 Database.rollback(sp); 50 ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,'插入联系人失败')); 51 return null; 52 } 53 try { 54 opportunity.accountId = account.id; 55 insert opportunity; 56 role.opportunityId = opportunity.id; 57 role.contactId = contact.id; 58 insert role; 59 } catch(Exception e) { 60 Database.rollback(sp); 61 ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,'插入机会失败')); 62 return null; 63 } 64 //跳转到新插入的opportunity的系统页面 65 PageReference opptyPage = new ApexPages.StandardController(opportunity).view(); 66 opptyPage.setRedirect(true); 67 return opptyPage; 68 } 69 }
2.NewOpportunityPage:填写企业信息,联系人信息和机会信息并实现提交code
1 <apex:page controller="newOpportunityController" tabStyle="Opportunity"> 2 3 <apex:sectionHeader title="New Customer Opportunity"/> 4 <apex:form id="theForm"> 5 <apex:pageMessages/> 6 <apex:pageBlock title="Customer Information" mode="edit"> 7 <apex:pageBlockSection title="Account Information"> 8 <apex:inputField id="accountName" value="{!account.name}"/> 9 <apex:inputField id="accountSite" value="{!account.site}"/> 10 </apex:pageBlockSection> 11 <apex:pageBlockSection title="Contact Information"> 12 <apex:inputField id="contactFirstName" value="{!contact.firstName}"/> 13 <apex:inputField id="contactLastName" value="{!contact.lastName}"/> 14 <apex:inputField id="contactPhone" value="{!contact.phone}"/> 15 </apex:pageBlockSection> 16 17 <apex:pageBlockSection title="Opportunity Information"> 18 <apex:inputField id="opportunityName" value="{!opportunity.name}"/> 19 <apex:inputField id="opportunityAmount" value="{!opportunity.amount}"/> 20 <apex:inputField id="opportunityCloseDate" value="{!opportunity.closeDate}"/> 21 <apex:inputField id="opportunityStageName" value="{!opportunity.stageName}"/> 22 <apex:inputField id="contactRole" value="{!role.role}"/> 23 </apex:pageBlockSection> 24 25 <apex:pageBlockButtons > 26 <apex:commandButton action="{!save}" value="Save" reRender="theForm"/> 27 </apex:pageBlockButtons> 28 29 </apex:pageBlock> 30 31 </apex:form> 32 </apex:page>
效果展现: orm
1.填写相关信息,提交表单,特地没有输入联系人,显示效果以下:对象
2.当对数据进行相关填充之后,结果以下:blog
再次保存之后提示不能对于已经有ID的对象执行insert操做的错误信息。当时没有太理解由于什么缘由致使了这种状况,后来joe给我答疑解惑,我才如梦初醒。当我对Account表执行了insert时,在事务尚未commit状况下,此条记录尚未存储到数据库中,可是controller中的对象便已经有了ID字段的值。当后期操做须要事务回滚时,数据库不保存insert进去的记录,可是此对象的ID却不会被清空,这就致使了下次insert此对象时,此对象已经有了ID,从而不能进行insert的操做了。同理,若是数据库没有当前的数据,对象却有ID,即便执行upsert操做也是会报相似的错误。事务
在咱们对相关级联表进行DML操做的时候,可使用clone操做,当回滚的时候,只是回滚数据库的内容,可是原来绑定到前台的对象并无生成相关的ID,从而能够摆脱上述的尴尬。对Controller层改造代码以下:get
1 public class newOpportunityController { 2 Account account; 3 Contact contact; 4 Opportunity opportunity; 5 OpportunityContactRole role; 6 7 public Account getAccount() { 8 if(account == null) 9 account = new Account(); 10 return account; 11 } 12 public Contact getContact() { 13 if(contact == null) 14 contact = new Contact(); 15 return contact; 16 } 17 public Opportunity getOpportunity() { 18 if(opportunity == null) 19 opportunity = new Opportunity(); 20 return opportunity; 21 } 22 public OpportunityContactRole getRole() { 23 if(role == null) 24 role = new OpportunityContactRole(); 25 return role; 26 } 27 28 public PageReference save() { 29 Savepoint sp = Database.setSavepoint(); 30 Account cloneAccount; 31 Contact cloneContact; 32 Opportunity cloneOpportunity; 33 OpportunityContactRole cloneRole; 34 try { 35 account.phone = contact.phone; 36 cloneAccount = account.clone(true); 37 insert cloneAccount; 38 } catch(Exception e) { 39 Database.rollback(sp); 40 ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,'插入企业失败')); 41 42 return null; 43 } 44 try { 45 if(contact.phone == null) { 46 Database.rollback(sp); 47 ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,'联系人电话不能为空')); 48 return null; 49 } 50 contact.accountId = cloneAccount.Id; 51 cloneContact = contact.clone(true); 52 insert cloneContact; 53 } catch(Exception e) { 54 Database.rollback(sp); 55 ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,'插入联系人失败')); 56 return null; 57 } 58 try { 59 opportunity.accountId = cloneAccount.id; 60 cloneOpportunity = opportunity.clone(false); 61 insert cloneOpportunity; 62 role.opportunityId = cloneOpportunity.id; 63 role.contactId = cloneContact.id; 64 cloneRole = role.clone(false); 65 insert cloneRole; 66 } catch(Exception e) { 67 Database.rollback(sp); 68 ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,'插入机会失败')); 69 return null; 70 } 71 //跳转到新插入的opportunity的系统页面 72 PageReference opptyPage = new ApexPages.StandardController(cloneOpportunity).view(); 73 opptyPage.setRedirect(true); 74 return opptyPage; 75 } 76 }
效果展现:input
1.当信息填写不完整状况下效果展现:
2.填好信息保存之后跳转到标准页面
总结:当对级联表进行操做的时候,必定要考虑一下当由于某些业务逻辑或者数据自身操做失败致使须要回滚状况下,致使数据库中不存在本条记录然然后台绑定的对象却相关复制的状况,若是编辑的case没有问题,可是涉及到新增的状况便暴露出来此问题了。篇中有描述错误的地方欢迎指出,有不懂得欢迎留言。除了使用clone操做之外应该还有其余的好操做能够避免此种事情的发生,若是有更好的操做,欢迎留言。