曾經(jīng)做項目沒有考慮那么多,對于級聯(lián)表操作都是正常的一步一步操作,沒有考慮過失敗情況,最近項目遇見了失敗的情況,導致碰到了相應的情況,特此mark一下,免得后期繼續(xù)踩坑。
需求如下:新建頁面,頁面中包含1.新建企業(yè),2.新建聯(lián)系人,3.新建機會。任何一步的邏輯或者DML操作失敗都會導致整體的回滾。只有當三步都正常插入成功了以后才會跳轉到新生成的機會的標準頁面。
1.NewOpportunityController:這里做了一個邏輯判斷,當聯(lián)系人為空情況下,不允許新建聯(lián)系人。當然,現(xiàn)實場景不會在這里判斷,但是現(xiàn)實場景會有很多的復雜的業(yè)務邏輯,這里只是簡單的處理。
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,'插入企業(yè)失敗'));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,'聯(lián)系人電話不能為空'));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,'插入聯(lián)系人失敗'));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的系統(tǒng)頁面65 PageReference opptyPage = new ApexPages.StandardController(opportunity).view(); 66 opptyPage.setRedirect(true); 67 return opptyPage;68 }69 }
2.NewOpportunityPage:填寫企業(yè)信息,聯(lián)系人信息和機會信息并實現(xiàn)提交
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>
效果展示:
1.填寫相關信息,提交表單,特意沒有輸入聯(lián)系人,顯示效果如下:
2.當對數(shù)據(jù)進行相關填充以后,結果如下:
再次保存以后提示不能對于已經(jīng)有ID的對象執(zhí)行insert操作的錯誤信息。當時沒有太理解因為什么原因導致了這種情況,后來joe給我答疑解惑,我才如夢初醒。當我對Account表執(zhí)行了insert時,在事務還沒有commit情況下,此條記錄還沒有存儲到數(shù)據(jù)庫中,但是controller中的對象便已經(jīng)有了ID字段的值。當后期操作需要事務回滾時,數(shù)據(jù)庫不保存insert進去的記錄,但是此對象的ID卻不會被清空,這就導致了下次insert此對象時,此對象已經(jīng)有了ID,從而不能進行insert的操作了。同理,如果數(shù)據(jù)庫沒有當前的數(shù)據(jù),對象卻有ID,即使執(zhí)行upsert操作也是會報類似的錯誤。
在我們對相關級聯(lián)表進行DML操作的時候,可以使用clone操作,當回滾的時候,只是回滾數(shù)據(jù)庫的內容,但是原來綁定到前臺的對象并沒有生成相關的ID,從而可以擺脫上述的尷尬。對Controller層改造代碼如下:
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,'插入企業(yè)失敗'));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,'聯(lián)系人電話不能為空'));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,'插入聯(lián)系人失敗'));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的系統(tǒng)頁面72 PageReference opptyPage = new ApexPages.StandardController(cloneOpportunity).view(); 73 opptyPage.setRedirect(true); 74 return opptyPage;75 }76 }
效果展示:
1.當信息填寫不完整情況下效果展示:
2.填好信息保存以后跳轉到標準頁面
總結:當對級聯(lián)表進行操作的時候,一定要考慮一下當因為某些業(yè)務邏輯或者數(shù)據(jù)自身操作失敗導致需要回滾情況下,導致數(shù)據(jù)庫中不存在本條記錄然而后臺綁定的對象卻相關復制的情況,如果編輯的case沒有問題,但是涉及到新增的情況便暴露出來此問題了。篇中有描述錯誤的地方歡迎指出,有不懂得歡迎留言。除了使用clone操作以外應該還有其他的好操作可以避免此種事情的發(fā)生,如果有更好的操作,歡迎留言。
作者:zero
博客地址:http://www.cnblogs.com/zero-zyq/
本文歡迎轉載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接
個人下載了一些相關學習的PDF文件,如果需要下載請訪問百度云 點擊此處訪問 密碼:jhuy
如果文章的內容對你有幫助,歡迎點贊~
http://www.cnblogs.com/zero-zyq/p/6778010.html