曾经做项目没有考虑那么多,对于级联表操作都是正常的一步一步操作,没有考虑过失败情况,最近项目遇见了失败的情况,导致碰到了相应的情况,特此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. elastic search 学习 一

    初步阅读了elastic search 的文档,并使用command实践操作. 大概明白其概念模型.

  2. 记录Winform开发过程中遇到的情况

    前两天开发了个Winform操作Excel和数据库的一个小程序,把Winform的一些东西又给捡了起来,当中又学到了一些新的东西,特来写出来留作纪念. 一.CSKIN美化框架的使用 刚开始做的时候,发 ...

  3. 文件IO理解

    一次读取写入单个字节 public class CopyFileDemo { public static void main(String[] args) throws IOException { F ...

  4. 用Qemu搭建aarch32学习环境

    作者信息 作者: 彭东林 邮箱: pengdonglin137@163.com QQ: 405728433 软件平台 主机: Ubuntu14.04 64位版本 模拟器:Qemu-2.8.0 Linu ...

  5. ES6 学习笔记(一)let,const和解构赋值

    let和const let和const是es6新增的两个变量声明关键字,与var的不同点在于: (1)let和const都是块级作用域,在{}内有效,这点在for循环中非常有用,只在循环体内有效.va ...

  6. 使用 ipdb 调试 Python

    1.安装 pip install ipdb 2.使用 python -m ipdb xxx.py 程序内部: from ipdb import set_trace set_trace() 3.常用命令 ...

  7. unity插件开发——AssetDatabase

    AssetDatebase也是一个静态类,他的作用是管理整个工程的所有文件(一般成为“资产”).直观地说就是管理整个project窗口中的所有内容,比如,你可以增加.删除.修改文件等等. 这里有几个常 ...

  8. [.NET] 一步步打造一个简单的 MVC 电商网站 - BooksStore(三)

    一步步打造一个简单的 MVC 电商网站 - BooksStore(三) 本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore &l ...

  9. ubuntu12.04下编译chrome

    1,直接下载压缩包: http://chromium-browser-source.commondatastorage.googleapis.com/chromium_tarball.html 2,安 ...

  10. TableView cell自适应高度-----xib

    1.通过xib创建一个cell,将label进行上左下右,进行适配, self.automaticallyAdjustsScrollViewInsets = NO; self.edgesForExte ...