曾經作項目沒有考慮那麼多,對於級聯表操做都是正常的一步一步操做,沒有考慮過失敗狀況,最近項目碰見了失敗的狀況,致使碰到了相應的狀況,特此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操做之外應該還有其餘的好操做能夠避免此種事情的發生,若是有更好的操做,歡迎留言。