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

相关文章

来自专栏黑泽君的专栏

day54_BOS项目_06

第一步:根据提供的 业务受理.pdm 文件生成建表文件 bos_qp.sql 第二步:由于业务受理.pdm 文件中有伪表,所以我们需要修改生成的建表文件,修改如...

762
来自专栏程序员的SOD蜜

PDF.NET SOD 开源框架红包派送活动 && 新手快速入门指引

一、框架的由来  快速入门 有关框架的更多信息,请看框架官方主页! 本套框架的思想是借鉴Java平台的Hibernate 和 iBatis 而来,兼有...

2779
来自专栏Java开发者杂谈

RocketMQ专题1:入门

​ 这里以RocketMQ的4.3.0版本为例,本地环境为windows10,jdk1.8, maven3.2.1.

1872
来自专栏移动端开发

iOS 测试三方 KIF 的那些事

一: KIF 三方库的配置       今天的广州天气还不错,原本想试试UI测试的,前几天也了解到很多公司都在用 KIF 这这三方框架!!今天也就试着做做,可就...

2176
来自专栏Golang语言社区

阅读源代码的姿势:以 go-restful 为例

一般初学者确定一个方向,比如web 后端、前端等,会选择一门编程语言深入下去,比如后端java、python、go等。通过项目不断练习编程语言和编程思维,知道如...

1242
来自专栏GopherCoder

『阅读源代码的姿势:以 go-restful 为例』

4373
来自专栏24K纯开源

Mac OS X平台下QuickLook开发教程

一、引言       Quick Look技术是Apple在Mac OS X 10.5中引入的一种用于快速查看文件内容的技术。用户只需要选中文件单击空格键即可快...

2758
来自专栏社区的朋友们

Node 架构从三层到 N 层,实现代码重用和解耦

三层架构通常意义上是将整个业务应用划分为:控制层、业务逻辑层以及数据访问层,三层架构在 Java Web 项目中很常见,那么这种架构能否运用在 Node 项目中...

1.7K1
来自专栏iOS122-移动混合开发研究院

写给iOS小白的MVVM教程(一): 从MVC到MVVM之一个典型的MVC应用场景

前言 本着实践为主的原则,此系列文章不做过多的概念性的阐述和讨论;更多的代码和篇幅用来展示MVC和MVVC下的基础代码结构与具体实现,来展示各自优劣.这篇文章,...

2747
来自专栏Java技术

Kafka设计解析(二)- Kafka High Availability (上)

http://www.jasongj.com/2015/04/24/KafkaColumn2/

1093

扫码关注云+社区