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

相关文章

来自专栏Porschev[钟慰]的专栏

Nodejs学习笔记(四)--- 与MySQL交互(felixge/node-mysql)

简介和安装   Node.js与MySQL交互操作有很多库,具体可以在 https://www.npmjs.org/search?q=mysql  查看。   ...

2979
来自专栏互联网杂技

SQL注入攻防入门详解

毕业开始从事winfrm到今年转到 web ,在码农届已经足足混了快接近3年了,但是对安全方面的知识依旧薄弱,事实上是没机会接触相关开发……必须的各种借口。这几...

35810
来自专栏应用案例

索引,视图,存储过程和触发器文档

实验案例一:验证索引的作用 1、首先创建一个数据量大的表,名称为“学生表”,分别有三列,学号,姓名和班级,如下图所示,学号为自动编号,班级为默认值“一班”。 ?...

1938
来自专栏纯洁的微笑

springboot(六):如何优雅的使用mybatis

这两天启动了一个新项目因为项目组成员一直都使用的是mybatis,虽然个人比较喜欢jpa这种极简的模式,但是为了项目保持统一性技术选型还是定了 mybatis。...

33312
来自专栏有趣的Python

Scrapy分布式爬虫打造搜索引擎 - (三)知乎网问题和答案爬取Python分布式爬虫打造搜索引擎

Python分布式爬虫打造搜索引擎 基于Scrapy、Redis、elasticsearch和django打造一个完整的搜索引擎网站 三、知乎网问题和答案爬取 ...

43110
来自专栏文渊之博

SQL Server 2016 行级别权限控制

背景 假如我们有关键数据存储在一个表里面,比如人员表中包含员工、部门和薪水信息。只允许用户访问各自部门的信息,但是不能访问其他部门。一般我们都是在程序端实现这个...

18310
来自专栏python学习路

一、Mysql(1)

  数据库简介 人类在进化的过程中,创造了数字、文字、符号等来进行数据的记录,但是承受着认知能力和创造能力的提升,数据量越来越大,对于数据的记录和准确查找,成为...

3379
来自专栏岑玉海

hbase源码系列(三)Client如何找到正确的Region Server

  客户端在进行put、delete、get等操作的时候,它都需要数据到底存在哪个Region Server上面,这个定位的操作是通过HConnection.l...

40911
来自专栏互联网杂技

SpringBoot (六) :如何优雅的使用 mybatis

这两天启动了一个新项目因为项目组成员一直都使用的是mybatis,虽然个人比较喜欢jpa这种极简的模式,但是为了项目保持统一性技术选型还是定了 mybatis。...

832
来自专栏张善友的专栏

SQL Injection的深入探讨

SQL injection可以说是一种漏洞,也可以说成是一种攻击方法,程序中的变量处理不当,对用户提交的数据过滤不足,都可能产生这个漏洞,而攻击原理就是利用用户...

1657

扫码关注云+社区