专栏首页salesforce零基础学习salesforce零基础学习(七十一)级联表DML操作

salesforce零基础学习(七十一)级联表DML操作

曾经做项目没有考虑那么多,对于级联表操作都是正常的一步一步操作,没有考虑过失败情况,最近项目遇见了失败的情况,导致碰到了相应的情况,特此mark一下,免得后期继续踩坑。

需求如下:新建页面,页面中包含1.新建企业,2.新建联系人,3.新建机会。任何一步的逻辑或者DML操作失败都会导致整体的回滚。只有当三步都正常插入成功了以后才会跳转到新生成的机会的标准页面。

1.NewOpportunityController:这里做了一个逻辑判断,当联系人为空情况下,不允许新建联系人。当然,现实场景不会在这里判断,但是现实场景会有很多的复杂的业务逻辑,这里只是简单的处理。

 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:填写企业信息,联系人信息和机会信息并实现提交

 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.填写相关信息,提交表单,特意没有输入联系人,显示效果如下:

2.当对数据进行相关填充以后,结果如下:

再次保存以后提示不能对于已经有ID的对象执行insert操作的错误信息。当时没有太理解因为什么原因导致了这种情况,后来joe给我答疑解惑,我才如梦初醒。当我对Account表执行了insert时,在事务还没有commit情况下,此条记录还没有存储到数据库中,但是controller中的对象便已经有了ID字段的值。当后期操作需要事务回滚时,数据库不保存insert进去的记录,但是此对象的ID却不会被清空,这就导致了下次insert此对象时,此对象已经有了ID,从而不能进行insert的操作了。同理,如果数据库没有当前的数据,对象却有ID,即使执行upsert操作也是会报类似的错误。

在我们对相关级联表进行DML操作的时候,可以使用clone操作,当回滚的时候,只是回滚数据库的内容,但是原来绑定到前台的对象并没有生成相关的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,'插入企业失败'));
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 }

效果展示:

1.当信息填写不完整情况下效果展示:

 2.填好信息保存以后跳转到标准页面

 总结:当对级联表进行操作的时候,一定要考虑一下当因为某些业务逻辑或者数据自身操作失败导致需要回滚情况下,导致数据库中不存在本条记录然而后台绑定的对象却相关复制的情况,如果编辑的case没有问题,但是涉及到新增的情况便暴露出来此问题了。篇中有描述错误的地方欢迎指出,有不懂得欢迎留言。除了使用clone操作以外应该还有其他的好操作可以避免此种事情的发生,如果有更好的操作,欢迎留言。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • salesforce 零基础学习(二十七)VF页面等待(loading)效果制作

    进行查询的情况下,显示友好的等待效果可以让用户更好的了解目前的状态以及减少用户消极的等待,例如下图所示。 ? VF提供了<apex:actionStatus>标...

    用户1169343
  • salesforce 零基础开发入门学习(六)简单的数据增删改查页面的构建

    VisualForce封装了很多的标签用来进行页面设计,本篇主要讲述简单的页面增删改查。使用的内容和设计到前台页面使用的标签相对简单,如果需要深入了解VF相关知...

    用户1169343
  • salesforce 零基础学习(五十)自定义View或者List以及查看系统原来的View或者List

    salesforce给我们提供了标准的页面,比如标准的页面包括标准的列表和标准的详细页视图。有的时候我们想要自定义视图,比如做一个项目的时候不希望使用者直接通过...

    用户1169343
  • Instagram 开源用于 Python 3的MonkeyType 工具

    Instagram 宣布将其 MonkeyType 工具开源。它是通过运行时跟踪类型自动将类型注释添加到您的 Python 3 代码的工具。 授权协议:BSD ...

    企鹅号小编
  • Livy Session 详解(下)

    即上图中的第 9 步中的 executeCodeFunc,用来真正运行代码片段的函数,流程如下

    codingforfun
  • 传统同步阻塞和异步非阻塞的区别理解

    同步与异步的理解 同步与异步的重点在消息通知的方式上,也就是调用结果通知的方式。 同步:当一个同步调用发出去后,调用者要一直等待调用结果的通知后,才能进行后续...

    小勇DW3
  • 谈谈 GitHub 开放私有仓库一事的影响

    GitHub 被资金雄厚的微软收购后,可运作资金增多,之前靠个人开发者和小团队所带来的收益便如同沧海一粟,有点微不足道了。并且在开放创建私有仓库之后,还可适度的...

    GitHubDaily
  • 小程序 · 一周报

    据 IT 之家消息 QQ 内测版 7.8.0 for Android 上线了 QQ 小程序集合「轻应用」,用户可通过聊天列表界面右上角的「+」号,看到 QQ 小...

    极乐君
  • Spring Boot 2.x 入门:构建第一个Web应用程序

    https://www.runoob.com/java/java-basic-syntax.html

    KenTalk
  • 用什么工具?做什么原型?给谁看?

    我们在日常的软件设计中经常会涉及到原型的设计。设计一个原型,无非就是三个目的:第一个目的是给自己看的,为了方便之后的下一步设计;第二个目的是给开发看,说服开发...

    奔跑的小鹿

扫码关注云+社区

领取腾讯云代金券