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

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

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

 public class newOpportunityController {
Account account;
Contact contact;
Opportunity opportunity;
OpportunityContactRole role; public Account getAccount() {
if(account == null)
account = new Account();
return account;
}
public Contact getContact() {
if(contact == null)
contact = new Contact();
return contact;
}
public Opportunity getOpportunity() {
if(opportunity == null)
opportunity = new Opportunity();
return opportunity;
}
public OpportunityContactRole getRole() {
if(role == null)
role = new OpportunityContactRole();
return role;
} public PageReference save() {
Savepoint sp = Database.setSavepoint();
try {
account.phone = contact.phone;
insert account;
} catch(Exception e) {
Database.rollback(sp);
ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,'插入企业失败')); return null;
}
try {
if(contact.phone == null) {
Database.rollback(sp);
ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,'联系人电话不能为空'));
return null;
}
contact.accountId = account.id;
insert contact;
} catch(Exception e) {
Database.rollback(sp);
ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,'插入联系人失败'));
return null;
}
try {
opportunity.accountId = account.id;
insert opportunity;
role.opportunityId = opportunity.id;
role.contactId = contact.id;
insert role;
} catch(Exception e) {
Database.rollback(sp);
ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,'插入机会失败'));
return null;
}
//跳转到新插入的opportunity的系统页面
PageReference opptyPage = new ApexPages.StandardController(opportunity).view();
opptyPage.setRedirect(true);
return opptyPage;
}
}

2.NewOpportunityPage:填写企业信息,联系人信息和机会信息并实现提交

 <apex:page controller="newOpportunityController" tabStyle="Opportunity"> 

     <apex:sectionHeader title="New Customer Opportunity"/>
<apex:form id="theForm">
<apex:pageMessages/>
<apex:pageBlock title="Customer Information" mode="edit">
<apex:pageBlockSection title="Account Information">
<apex:inputField id="accountName" value="{!account.name}"/>
<apex:inputField id="accountSite" value="{!account.site}"/>
</apex:pageBlockSection>
<apex:pageBlockSection title="Contact Information">
<apex:inputField id="contactFirstName" value="{!contact.firstName}"/>
<apex:inputField id="contactLastName" value="{!contact.lastName}"/>
<apex:inputField id="contactPhone" value="{!contact.phone}"/>
</apex:pageBlockSection> <apex:pageBlockSection title="Opportunity Information">
<apex:inputField id="opportunityName" value="{!opportunity.name}"/>
<apex:inputField id="opportunityAmount" value="{!opportunity.amount}"/>
<apex:inputField id="opportunityCloseDate" value="{!opportunity.closeDate}"/>
<apex:inputField id="opportunityStageName" value="{!opportunity.stageName}"/>
<apex:inputField id="contactRole" value="{!role.role}"/>
</apex:pageBlockSection> <apex:pageBlockButtons >
<apex:commandButton action="{!save}" value="Save" reRender="theForm"/>
</apex:pageBlockButtons> </apex:pageBlock> </apex:form>
</apex:page>

效果展示:

1.填写相关信息,提交表单,特意没有输入联系人,显示效果如下:

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

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

在我们对相关级联表进行DML操作的时候,可以使用clone操作,当回滚的时候,只是回滚数据库的内容,但是原来绑定到前台的对象并没有生成相关的ID,从而可以摆脱上述的尴尬。对Controller层改造代码如下:

 public class newOpportunityController {
Account account;
Contact contact;
Opportunity opportunity;
OpportunityContactRole role; public Account getAccount() {
if(account == null)
account = new Account();
return account;
}
public Contact getContact() {
if(contact == null)
contact = new Contact();
return contact;
}
public Opportunity getOpportunity() {
if(opportunity == null)
opportunity = new Opportunity();
return opportunity;
}
public OpportunityContactRole getRole() {
if(role == null)
role = new OpportunityContactRole();
return role;
} public PageReference save() {
Savepoint sp = Database.setSavepoint();
Account cloneAccount;
Contact cloneContact;
Opportunity cloneOpportunity;
OpportunityContactRole cloneRole;
try {
account.phone = contact.phone;
cloneAccount = account.clone(true);
insert cloneAccount;
} catch(Exception e) {
Database.rollback(sp);
ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,'插入企业失败')); return null;
}
try {
if(contact.phone == null) {
Database.rollback(sp);
ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,'联系人电话不能为空'));
return null;
}
contact.accountId = cloneAccount.Id;
cloneContact = contact.clone(true);
insert cloneContact;
} catch(Exception e) {
Database.rollback(sp);
ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,'插入联系人失败'));
return null;
}
try {
opportunity.accountId = cloneAccount.id;
cloneOpportunity = opportunity.clone(false);
insert cloneOpportunity;
role.opportunityId = cloneOpportunity.id;
role.contactId = cloneContact.id;
cloneRole = role.clone(false);
insert cloneRole;
} catch(Exception e) {
Database.rollback(sp);
ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,'插入机会失败'));
return null;
}
//跳转到新插入的opportunity的系统页面
PageReference opptyPage = new ApexPages.StandardController(cloneOpportunity).view();
opptyPage.setRedirect(true);
return opptyPage;
}
}

效果展示:

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

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

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

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

  1. salesforce 零基础学习(三十七) DML及Database方法简单描述

    在apex中通过soql查询可以使用两种方式,使用DML语句或者使用Database的方法. 使用DML语句和使用Database类的方法对于我们来说用的都很多,并且都很常见.对于数据库常见的操作:增 ...

  2. salesforce零基础学习(一百一十一)custom metadata type数据获取方式更新

    本篇参考: https://developer.salesforce.com/docs/atlas.en-us.234.0.apexref.meta/apexref/apex_methods_syst ...

  3. salesforce零基础学习(八十)使用autoComplete 输入内容自动联想结果以及去重实现

    项目中,我们有时候会需要实现自动联想功能,比如我们想输入用户或者联系人名称,去联想出系统中有的相关的用户和联系人,当点击以后获取相关的邮箱或者其他信息等等.这种情况下可以使用jquery ui中的au ...

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

    注:本篇解决方案内容实现转自:http://mysalesforceescapade.blogspot.com/2015/03/getting-dependent-picklist-values-fr ...

  5. 【转】【Salesforce】salesforce 零基础学习(十七)Trigger用法

    看本篇之前可以相应阅读以下Trigger相关文章: 1.https://developer.salesforce.com/page/Trigger_Frameworks_and_Apex_Trigge ...

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

    项目中,审批操作无处不在.配置审批流时,我们有时候会用到queue,related user设置当前步骤的审批人,审批人可以一个或者多个.当审批人有多个时,邮件中获取当前记录的审批人和审批意见就不能随 ...

  7. salesforce零基础学习(九十六)Platform Event浅谈

    本篇参考:https://developer.salesforce.com/blogs/2018/07/which-streaming-event-do-i-use.html https://trai ...

  8. salesforce零基础学习(一百零五)Change Data Capture

    本篇参考: https://developer.salesforce.com/docs/atlas.en-us.232.0.api_streaming.meta/api_streaming/using ...

  9. salesforce零基础学习(一百一十三)Trigger中获取IP地址的过程

    本篇参考: https://developer.salesforce.com/docs/atlas.en-us.228.0.apexcode.meta/apexcode/apex_class_Auth ...

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

    第十七篇的Trigger用法为通过Handler方式实现Trigger的封装,此种好处是一个Handler对应一个sObject,使本该在Trigger中写的代码分到Handler中,代码更加清晰. ...

随机推荐

  1. Octave Tutorial(《Machine Learning》)之第一课《数据表示和存储》

    Octave Tutorial 第一课 Computation&Operation 数据表示和存储 1.简单的四则运算,布尔运算,赋值运算(a && b,a || b,xor( ...

  2. Mac系统安装nginx+rtmp模块

    1.安装命令 ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install) ...

  3. Repaints and Reflows 重绘和重排版

    当浏览器下载完所有页面HTML标记,JavaScript,CSS,图片之后,它解析文件并创建两个内部数据 一棵DOM树 表示页面结构 Normal 0 7.8 磅 0 2 false false fa ...

  4. 使用moment.js轻松管理日期和时间

    大家在前端Javascript开发中会遇到处理日期时间的问题,经常会拿来一大堆处理函数才能完成一个简单的日期时间显示效果.今天我给大家介绍一个轻量级的Javascript日期处理类库:moment.j ...

  5. 在腾讯云上部署Hexo博客

    推荐理由 ----搭建个人的空间博客目前深受个人开发者的追捧,然而博客的种类和平台有很多,Hexo是一个开源的静态博客生成器.相比于其他博客而言它只要是web容器就能用.除了闷头专研技术之外,程序员还 ...

  6. 第2章Zabbix基础进阶

    p.MsoNormal,li.MsoNormal,div.MsoNormal { margin: 0cm; margin-bottom: .0001pt; text-align: justify; t ...

  7. AngularJS1.X学习笔记1-整体看看

    听说 明天是愚人节,这与我有什么关系呢!我可 不想被愚弄,但是但是,我这么笨怎么才能不被愚弄呢?左思右想,我决定从现在开始闭关,闭关干啥哩?学习!学习AngularJS.以前学习过Angular的,不 ...

  8. Codeforces 392C Yet Another Number Sequence (矩阵快速幂+二项式展开)

    题意:已知斐波那契数列fib(i) , 给你n 和 k , 求∑fib(i)*ik (1<=i<=n) 思路:不得不说,这道题很有意思,首先我们根据以往得出的一个经验,当我们遇到 X^k ...

  9. Struts2基础学习(六)—文件的上传和下载

    一.文件的上传 1.单个文件上传      Struts2使用拦截器完成了文件的上传,而且底层使用的也是FileUpload开源组件. 客户端注意事项: (1)method="post&qu ...

  10. 实现高效的GPRS驱动程序

    1. 引言 用过几款GPRS模块,也从淘宝上买过多个GPRS模块,一般的都会送一个驱动程序和使用demo,但是代码质量都较低. 回头看了下几年前使用的GPRS代码,从今天的角度来看,也就是买模块赠送一 ...