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操作以外应该还有其他的好操作可以避免此种事情的发生,如果有更好的操作,欢迎留言。

时间: 2024-10-14 00:55:19

salesforce零基础学习(七十一)级联表DML操作的相关文章

salesforce零基础学习(八十七)Apex 中Picklist类型通过Control 字段值获取Dependent List 值

注:本篇解决方案内容实现转自:http://mysalesforceescapade.blogspot.com/2015/03/getting-dependent-picklist-values-from.html 群里面有个小伙伴问了一个关于两个有Dependence关系的Picklist字段如何在Apex中通过control字段的值获取到Dependence字段的值,针对Salesforce配置来说,我们很好配置出两个Dependence字段的关系,通过点击设置一下include关系即可.如

salesforce零基础学习(八十二)审批邮件获取最终审批人和审批意见

项目中,审批操作无处不在.配置审批流时,我们有时候会用到queue,related user设置当前步骤的审批人,审批人可以一个或者多个.当审批人有多个时,邮件中获取当前记录的审批人和审批意见就不能随便的取一个审批人了,有以下方式针对不同的场景可以获取到当前记录的最终审批人以及审批意见. 邮件内容使用以下几种方式实现: 1.代码里面实现邮件发送 2.email template(text/html/custom) 3.visualforce emailTemplate 对发送邮件方式不清楚的,可

salesforce 零基础学习(五十二)Trigger使用篇(二)

第十七篇的Trigger用法为通过Handler方式实现Trigger的封装,此种好处是一个Handler对应一个sObject,使本该在Trigger中写的代码分到Handler中,代码更加清晰. 十七篇链接:salesforce 零基础学习(十七)Trigger用法 有的时候对于sObject的trigger处理复杂的情况下,比如一个sObject的before update要实现功能1,2.....n功能情况下,Handler中需要在before update写实现功能1--n的代码.然而

salesforce零基础学习(八十九)使用 input type=file 以及RemoteAction方式上传附件

在classic环境中,salesforce提供了<apex:inputFile>标签用来实现附件的上传以及内容获取.salesforce 零基础学习(二十四)解析csv格式内容中有类似的使用此标签进行解析附件内容,后台只要声明String类型变量用来存储附件名称,Blob类型变量用来存储附件的内容即可. 但是当我们的项目整体使用第三方的前端框架,例如VUE或者angular等前端框架时,有时使用apex:inputFile反而不是很方便,需要用到html的原生的附件上传的标签<inpu

salesforce零基础学习(七十八)线性表链形结构简单实现

前两篇内容为栈和队列的顺序结构的实现,栈和队列都是特殊的线性表,线性表除了有顺序结构以外,还有线性结构. 一.线性表的链形结构--链表 使用顺序存储结构好处为实现方式使用数组方式,顺序是固定的.所以查询某个位置的元素特别容易,时间复杂度为O(1),但是当增加或者删除时,会需要将操作元素后面的元素整体向左或者向右平移.时间复杂度为O(n).所以当线性表查询操作多于增删操作,优先使用顺序存储结构的线性表:当线性表增删操作多于查询操作,则优先使用链式存储结构的线性表. 线性表的链式存储结构的特点是用一

salesforce零基础学习(八十五)streaming api 简单使用(接近实时获取你需要跟踪的数据的更新消息状态)

Streaming API参考链接: https://trailhead.salesforce.com/en/modules/api_basics/units/api_basics_streaming https://resources.docs.salesforce.com/210/latest/en-us/sfdc/pdf/api_streaming.pdf 背景:工作中我们有可能会有这样相关的需求:某些数据很重要,需要实时监控是否有变化,或者某些数据在其他的平台有集成.如果有变化,不刷新页

salesforce零基础学习(九十二)使用Ant Migration Tool 实现Metadata迁移

我们在做项目时经常会使用changeset作为部署工具,但是某些场景使用changeset会比较难操作,比如当我们在sandbox将apex class更改名字想要部署到生产的org或者其他环境的org,使用changeset是没法实现的,这个时候我们需要使用Ant Migration Tool还是更好的. salesforce基于metadata进行管理,Ant Migration Tool 是一个基于 Java/Ant的命令行工具用于将metadata从本地迁移至Sales Org. Mig

salesforce 零基础学习(四十一)Group

salesforce中,有的时候我们需要将一组用户放进一个Group,用来实现以下主要功能: 1.通过sharing rule设置默认的共享访问; 2.将记录分享给其他用户; 3.指定同步的联系人,这些联系人的onwer属于其他user; 4.分配Group中用户到指定的动作,比如审批流程等. Group作为系统表,其字段信息如下所示: 其中Type作为PickList字段,他的取值可以通过Schema方式取出. public with sharing class GroupPickList {

salesforce零基础学习(八十一)更改标准字段的label名称(Admin)

我们在开发中往往需要考虑国际化功能,salesforce 提供了国际化功能,在search部分搜索translate,便可以找到translate部分,从而对需要的进行translate.比如picklist values的中英文,字段标题的中英文翻译等等 翻译部分有一项可以设置需要翻译的类型,有时候我们需要对标准表的标准字段进行中英文翻译,但是发现里面并没有translate的地方.比如Account的Name字段是标准的字段,label的名字为Name,中文对应为客户名.但是用户希望将Nam