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 条评论
登录 后参与评论

相关文章

来自专栏精讲JAVA

Java 虚拟机 3:常用 JVM 命令参数

之后写的东西就会用到虚拟机参数了,现在这里汇个总自己平时用到的、看到的一些虚拟机参数。现在看不懂没关系,反正之后都会用到的:

802
来自专栏知识分享

18-GPRS(Air202)拨打电话

源码打包出售  https://item.taobao.com/item.htm?spm=a2126o.11854294.0.0.3e3a4831ODxjIk...

662
来自专栏Jerry的SAP技术分享

观察者模式在One Order回调函数中的应用

例如需求是搞清楚function module CRM_PRODUCT_I_A_CHANGE_ORGM_EC在什么样的场景下会被调用。当然最费时间的做法是设一个...

3468
来自专栏技术翻译

关于Couchbase-Dzone数据库,你必须了解的10件事情

此功能已经存在了一段时间,但仍值得一提。一些Key-Value Store只允许你将整个文档全部整合在一起,这是一个合理的。但是,如果你使用Couchbase作...

440
来自专栏进击的程序猿

orm 系列 之 常用设计模式 The Repository Pattern

本文是orm系列的第一篇,内容来自github上的一个Markdown,清晰的讲述了一些数据库设计上常用的设计模式,并且阐述了orm是什么?

1173
来自专栏云计算教程系列

如何使用腾讯云云硬盘API

腾讯云控制台允许您以类似于使用硬盘驱动器的方式管理腾讯云CVM的额外存储。只需点击腾讯云简化的GUI或图形用户界面,即可为我们的CVM添加云硬盘。但是,这不是一...

1082
来自专栏进击的程序猿

orm 系列 之 Eloquent演化历程2

上篇讲到了数据库Relation的实现,本篇接着讲migrations or database modification logic的功能,此处开始的git是g...

663
来自专栏码神联盟

碎片化 | 第四阶段-48-hibernate概述和配置-视频

本套视频从Java基础到架构模式以及AI算法,整体视频以“碎片化”学习的模式,提供给大家 ,并配备实际项目为案例,让大家在坐车、吃饭、午休、蹲坑的时候,都可以学...

3196
来自专栏数据结构与算法

P1165 日志分析

题目描述 M 海运公司最近要对旗下仓库的货物进出情况进行统计。目前他们所拥有的唯一记录就是一个记录集装箱进出情况的日志。该日志记录了两类操作:第一类操作为集装箱...

33210
来自专栏坚毅的PHP

PHP码农在Golang压力下的生存之道-PHP性能优化实践

随着国内Golang的火爆,phper的生存压力越来越大,在一次内部技术讨论中,gopher甚至提出,要什么php,写php的全部开掉,唉,码农何苦为难码农。 ...

8358

扫码关注云+社区