您可以在 Lightning 平台持久性层中添加数据并与之交互。这 sObject 数据类型是保存数据对象的主要数据类型。您将使用数据 操作语言 (DML) 用于处理数据,并使用查询语言检索数据, 例如(),等等。
- 使用 sObjects
在本开发人员指南中,该术语是指可以存储在 Lightning 平台数据库中的任何对象。sObject - 数据操作语言
Apex 使您能够在数据库中插入、更新、删除或还原数据。DML 操作允许您一次修改一条记录或批量修改记录。 - SOQL 和 SOSL 查询
您可以在 Apex 中即时评估 Salesforce 对象查询语言 (SOQL) 或 Salesforce 对象搜索语言 (SOSL) 语句,方法是将语句括在方括号中。 - SOQL For 循环 SOQL 循环遍历
SOQL 查询返回的所有 sObject 记录。for - sObject 集合
可以管理列表、集和映射中的 sObject。 - 动态Apex
- Apex 安全和共享
当您使用 Apex 时,代码的安全性至关重要。您需要为 Apex 类添加用户权限并强制执行共享规则。请继续阅读,了解 Apex 托管共享并获取一些安全提示。 - 自定义设置 自定义设置
类似于自定义对象。应用程序开发人员可以创建自定义数据集,并为组织、配置文件或特定用户关联自定义数据。所有自定义设置数据都公开在应用程序缓存中,这样就可以进行高效访问,而无需重复查询数据库。然后,公式字段、验证规则、流、Apex 和 SOAP API 可以使用此数据。
使用 sObjects
在本开发人员指南中,该术语是指任何对象 可以存储在 Lightning 平台数据库中。
sObject
- sObject 类型
sObject 变量表示一行数据,只能在 Apex 中使用对象的 SOAP API 名称声明。 - 访问 SObject 字段
- 验证 sObjects 和字段
sObject 类型
sObject 变量表示一行数据,只能在 Apex 中使用 对象的 SOAP API 名称。
例如:
Account a = new Account();
MyCustomObject__c co = new MyCustomObject__c();
与 SOAP API 类似,Apex 允许使用泛型 sObject 抽象类型来 表示任何对象。sObject 数据类型可用于处理不同 sObject 的类型。
操作人员仍然需要混凝土 sObject 类型,因此所有实例都是特定的 sObject。例如:new
sObject s = new Account();
还可以在泛型 sObject 类型和特定 sObject 类型之间使用强制转换。 例如:
// Cast the generic variable s from the example above
// into a specific account and account variable a
Account a = (Account)s;
// The following generates a runtime error
Contact c = (Contact)s;
由于 sObject 的工作方式与对象类似,因此您还可以将 以后:
Object obj = s;
// and
a = (Account)obj;
DML 操作适用于声明为泛型 sObject 数据类型的变量以及 与常规 sObjects。
sObject 变量初始化为 ,但 可以使用运算符分配有效的对象引用。例如:nullnew
Account a = new Account();
开发人员还可以在实例化新的 s对象。例如:name = value
Account a = new Account(name = 'Acme', billingcity = 'San Francisco');
有关从 Lightning 平台数据库访问现有 sObject 的信息,请参阅 SOQL 和 SOSL 参考中的“SOQL 和 SOSL 查询”。
注意
当对象记录 最初是第一次插入到数据库中。有关详细信息,请参阅列表。
自定义标签
自定义标签 不是标准的 sObject。您无法创建自定义标签的新实例。您可以 仅使用 访问自定义标签的值。为 例:
system.label.label_name
String errorMsg = System.Label.generic_error;
为 有关自定义标签的详细信息,请参阅 Salesforce 中的“自定义标签” 帮助。
访问 SObject 字段
与 Java 一样,可以使用简单的点表示法访问或更改 SObject 字段。为 例:
Account a = new Account();
a.Name = 'Acme'; // Access the account name field and assign it 'Acme'
系统生成的字段,例如“创建者”或“上次修改时间” 日期,无法修改。如果尝试,Apex 运行时引擎会生成错误。 此外,公式字段值和其他字段的值对 无法更改上下文用户。
如果使用泛型 SObject 类型而不是特定对象(如 Account),则可以 仅使用点表示法检索 Id 字段。您可以为使用 Salesforce API 版本 27.0 保存的 Apex 代码设置“Id”字段,并且 稍后)。或者,您可以使用泛型 SObject 和方法。请参见SObject 类。putget
此示例演示如何访问 Id 字段和操作 不允许在通用 SObject 上使用。
Account a = new Account(Name = 'Acme', BillingCity = 'San Francisco');
insert a;
sObject s = [SELECT Id, Name FROM Account WHERE Name = 'Acme' LIMIT 1];
// This is allowed
ID id = s.Id;
// The following line results in an error when you try to save
String x = s.Name;
// This line results in an error when you try to save using API version 26.0 or earlier
s.Id = [SELECT Id FROM Account WHERE Name = 'Acme' LIMIT 1].Id;
注意
如果您的组织已启用个人帐户,则您有两种不同类型的 帐户:企业帐户和个人帐户。如果您的代码使用 创建一个新帐户,则会创建一个业务帐户。如果您的代码 使用时,将创建一个个人帐户。nameLastName如果要对 SObject 进行操作,建议先进行转换 添加到特定对象中。为 例:
Account a = new Account(Name = 'Acme', BillingCity = 'San Francisco');
insert a;
sObject s = [SELECT Id, Name FROM Account WHERE Name = 'Acme' LIMIT 1];
ID id = s.ID;
Account convertedAccount = (Account)s;
convertedAccount.name = 'Acme2';
update convertedAccount;
Contact sal = new Contact(FirstName = 'Sal', Account = convertedAccount);
以下示例演示如何对一组记录使用 SOSL 来确定其 对象类型。将通用 SObject 记录转换为联系人、潜在顾客或 帐户,您可以修改其字段 因此:
public class convertToCLA {
List<Contact> contacts = new List<Contact>();
List<Lead> leads = new List<Lead>();
List<Account> accounts = new List<Account>();
public void convertType(String phoneNumber) {
List<List<SObject>> results = [FIND :phoneNumber
IN Phone FIELDS
RETURNING Contact(Id, Phone, FirstName, LastName),
Lead(Id, Phone, FirstName, LastName),
Account(Id, Phone, Name)];
List<SObject> records = new List<SObject>();
records.addAll(results[0]); //add Contact results to our results super-set
records.addAll(results[1]); //add Lead results
records.addAll(results[2]); //add Account results
if (!records.isEmpty()) {
for (Integer i = 0; i < records.size(); i++) {
SObject record = records[i];
if (record.getSObjectType() == Contact.sObjectType) {
contacts.add((Contact) record);
} else if (record.getSObjectType() == Lead.sObjectType){
leads.add((Lead) record);
} else if (record.getSObjectType() == Account.sObjectType) {
accounts.add((Account) record);
}
}
}
}
}
使用 SObject 字段
SObject 字段可以初始设置,也可以不设置(未设置);未设置的字段与 空字段或空白字段。在 SObject 上执行 DML 操作时,可以更改字段 这是设置的;您无法更改未设置的字段。
注意
若要擦除字段的当前值,请将该字段设置为 null。
如果 Apex 方法采用 SObject 参数,则可以使用 System.isSet() 方法标识设置字段。如果你 想要取消设置任何字段以保留其值,请先创建一个 SObject 实例。然后 仅应用要成为 DML 操作一部分的字段。
此示例代码演示如何将 SObject 字段标识为已设置或未设置。
Contact nullFirst = new Contact(LastName='Codey', FirstName=null);
System.assertEquals(true, nullFirst.isSet('FirstName'), 'FirstName is set to a literal value, so it counts as set');
Contact unsetFirst = new Contact(LastName='Astro');
System.assertEquals(false, unsetFirst.isSet('FirstName'), ‘FirstName is not set’);
仅当 SObject 的 SObject 字段类型为 Boolean 时,表达式的计算结果才为 true 字段为 true。如果字段为 false 或 null,则表达式的计算结果为 false。这 示例代码显示了一个表达式,用于检查Campaign对象的字段是否为null。因为这个表达式总是 计算结果为 false,语句中的代码为 从未执行过。IsActiveif
Campaign cObj= new Campaign();
...
if (cObj.IsActive == null) {
... // IsActive is evaluated to false and this code block is not executed.
}
验证 sObjects 和字段
解析和验证 Apex 代码时,将针对所有 sObject 和字段引用进行验证 实际的对象和字段名称,当无效名称 使用。此外,Apex 解析器还跟踪使用的自定义对象和字段,这两个对象和字段都在 代码的语法以及嵌入的 SOQL 和 SOSL 语句。
平台会阻止用户在进行以下类型的修改时进行这些更改 导致 Apex 代码无效:
- 更改字段或对象名称
- 从一种数据类型转换为另一种数据类型
- 删除字段或对象
- 进行某些组织范围的更改,例如记录共享、字段历史记录跟踪或 记录类型
数据操作语言
Apex 使您能够在数据库中插入、更新、删除或还原数据。DML系列 操作允许您一次修改一条记录或批量修改记录。
- DML 的工作原理
- 使用 DML
添加和检索数据 Apex 与 Lightning Platform 持久性层紧密集成。数据库中的记录可以使用简单的语句直接通过 Apex 插入和操作。Apex 中允许您在数据库中添加和管理记录的语言是数据操作语言 (DML)。与用于读取操作(查询记录)的 SOQL 语言相比,DML 用于写入操作。 - DML 语句与数据库类方法 Apex 提供了两种执行 DML 操作的方法:使用 DML 语句或 Database 类方法
。这为执行数据操作的方式提供了灵活性。DML 语句使用起来更简单,并导致可以在代码中处理的异常。 - 作为原子事务的 DML 操作
- DML 操作
使用 DML,您可以插入新记录并将其提交到数据库。您还可以更新现有记录的字段值。 - 异常处理
- 有关 DML
的更多信息 以下是您可能想知道的有关使用数据操作语言的一些事项。 - 锁定记录
锁定 sObject 记录时,不允许其他客户端或用户通过代码或 Salesforce 用户界面进行更新。锁定记录的客户端可以对记录执行逻辑并进行更新,并保证锁定的记录在锁定期间不会被其他客户端更改。
DML 的工作原理
单个 DML 操作与批量 DML 操作
您可以对单个 sObject 或批量执行 DML 操作 在 sObject 列表中。建议执行批量 DML 操作,因为 它有助于避免达到调控器限制,例如每个 150 个语句的 DML 限制 顶点交易。此限制旨在确保公平访问共享资源 在闪电平台中。对 sObjects 计数列表执行 DML 操作 作为一个 DML 语句,而不是每个 sObject 的一个语句。
此示例对单个 sObject 执行 DML 调用,效率不高。
循环遍历联系人。对于每个 联系人,如果部门字段与某个值匹配,则为 Description__c字段。如果列表包含的项目不止,则第 151 次更新 返回无法捕获的异常。for
List<Contact> conList = [Select Department , Description from Contact];
for(Contact badCon : conList) {
if (badCon.Department == 'Finance') {
badCon.Description__c = 'New description';
}
// Not a good practice since governor limits might be hit.
update badCon;
}
此示例是上一个示例的修改版本,该示例未命中 调速器限制。DML 操作是通过调用联系人列表批量执行的。此代码计数 作为一个 DML 语句,远低于 150 的限制。update
// List to hold the new contacts to update.
List<Contact> updatedList = new List<Contact>();
List<Contact> conList = [Select Department , Description from Contact];
for(Contact con : conList) {
if (con.Department == 'Finance') {
con.Description = 'New description';
// Add updated contact sObject to the list.
updatedList.add(con);
}
}
// Call update on the list of contacts.
// This results in one DML call for the entire list.
update updatedList;
另一个 DML 调控器限制是 DML 可以处理的总行数 单个事务中的操作,即 10,000 个。所有 DML 处理的所有行 同一事务计数的调用将递增到此限制。例如,如果 您在同一笔交易中插入 100 个联系人并更新 50 个联系人,您的总数 DML 处理的行数为 150。您还剩下 9,850 行 (10,000 – 150)。
系统上下文和共享规则
大多数 DML 操作在系统上下文中执行,忽略当前用户的 权限、字段级安全性、组织范围的默认值、角色中的位置 层次结构和共享规则。有关详细信息,请参阅强制执行共享规则。
注意
如果您在匿名块中执行 DML 操作,则它们将使用 当前用户的对象级和字段级权限。
最佳实践
使用 SObjects 上的 DML,最好构造新实例并仅更新字段 您希望在不查询其他字段的情况下进行修改。如果查询 要更新的字段,您可以还原查询的字段值,这些字段值可能具有 在查询和 DML 之间更改。
使用 DML 添加和检索数据
Apex 与 Lightning Platform 持久性层紧密集成。记录 可以使用简单的语句直接通过 Apex 插入和操作数据库。 Apex 中允许您在数据库中添加和管理记录的语言是数据 操作语言 (DML)。与用于读取的 SOQL 语言相比 操作(查询记录),DML 用于写入操作。
在插入或操作记录之前,记录数据在内存中作为 sObject 创建。 sObject 数据类型是泛型数据类型,对应于 将保存记录数据的变量。有特定的数据类型,子类型从 sObject 数据类型,对应于标准对象记录的数据类型,例如 作为 Account 或 Contact,以及自定义对象,例如 Invoice_Statement__c。通常,您 将处理这些特定的 sObject 数据类型。但有时,当你不这样做时 提前知道 sObject 的类型,就可以使用通用的 sObject 数据了 类型。这是一个示例,说明如何创建新的特定帐户 sObject 并分配 它设置为变量。
Account a = new Account(Name='Account Example');
在前面的示例中,变量引用的帐户存在于内存中,并带有必填字段。但是,它尚未持久化到 Lightning 平台持久性层。您需要调用 DML 语句来持久化 s对象添加到数据库。下面是创建和保留此帐户的示例 使用语句。aNameinsert
Account a = new Account(Name='Account Example');
insert a;
此外,还可以使用 DML 修改已插入的记录。其中 您可以执行的操作包括记录更新、删除、从 回收站、合并记录或转换潜在客户。查询记录后,您将获得 sObject 实例,您可以修改这些实例,然后保留其更改。这是一个 查询以前保留的现有记录的示例,更新 内存中此记录的 sObject 表示形式上的几个字段,然后 将此更改保存到数据库。
// Query existing account.
Account a = [SELECT Name,Industry
FROM Account
WHERE Name='Account Example' LIMIT 1];
// Write the old values the debug log before updating them.
System.debug('Account Name before update: ' + a.Name); // Name is Account Example
System.debug('Account Industry before update: ' + a.Industry);// Industry is not set
// Modify the two fields on the sObject.
a.Name = 'Account of the Day';
a.Industry = 'Technology';
// Persist the changes.
update a;
// Get a new copy of the account from the database with the two fields.
Account a = [SELECT Name,Industry
FROM Account
WHERE Name='Account of the Day' LIMIT 1];
// Verify that updated field values were persisted.
System.assertEquals('Account of the Day', a.Name);
System.assertEquals('Technology', a.Industry);
DML 语句与数据库类方法
Apex 提供了两种执行 DML 操作的方法:使用 DML 语句或数据库 类方法。这为执行数据操作的方式提供了灵活性。DML 语句 更易于使用,并导致您可以在 法典。
这是 用于插入新记录的 DML 语句。
// Create the list of sObjects to insert
List<Account> acctList = new List<Account>();
acctList.add(new Account(Name='Acme1'));
acctList.add(new Account(Name='Acme2'));
// DML statement
insert acctList;
这是上一个示例的等效示例,但它使用了 Database 的方法 类而不是 DML 谓词。
// Create the list of sObjects to insert
List<Account> acctList = new List<Account>();
acctList.add(new Account(Name='Acme1'));
acctList.add(new Account(Name='Acme2'));
// DML statement
Database.SaveResult[] srList = Database.insert(acctList, false);
// Iterate through each returned result
for (Database.SaveResult sr : srList) {
if (sr.isSuccess()) {
// Operation was successful, so get the ID of the record that was processed
System.debug('Successfully inserted account. Account ID: ' + sr.getId());
}
else {
// Operation failed, so get all errors
for(Database.Error err : sr.getErrors()) {
System.debug('The following error has occurred.');
System.debug(err.getStatusCode() + ': ' + err.getMessage());
System.debug('Account fields that affected this error: ' + err.getFields());
}
}
}
这两个选项之间的一个区别是,通过使用 Database 类方法,您可以 可以指定在出现错误时是否允许部分记录处理 遇到。您可以通过传递额外的第二个布尔参数来实现此目的。如果你 指定此参数,如果 a 记录 失败,其余的 DML 操作仍然可以成功。此外,除了例外,一个 result 对象数组(如果只传入一个 sObject,则返回一个 result 对象) 包含每个操作的状态和遇到的任何错误。默认情况下,此 可选参数是 ,这意味着如果 至少一个 sObject 无法处理,所有剩余的 sObject 都不会处理,并且 对于导致失败的记录,将引发异常。falsetrue
以下内容可帮助您决定何时要使用 DML 语句或 Database 类 方法。
- 如果您希望在批量 DML 处理期间发生的任何错误,请使用 DML 语句 作为 Apex 异常引发,该异常会立即中断控制流(通过使用块)。此行为是 与大多数数据库过程语言中处理异常的方式类似。try. . .catch
- 如果要允许批量 DML 部分成功,请使用 Database 类方法 操作 – 如果记录失败,DML 操作的其余部分仍可 成功。然后,应用程序可以检查被拒绝的记录,并可能重试 操作。使用此窗体时,可以编写从不引发 DML 的代码 异常错误。相反,您的代码可以使用适当的 results 数组来判断 成功或失败。请注意,Database 方法还包括支持 引发异常,类似于 DML 语句。
注意
除了少数操作外,大多数操作在两者之间重叠。
- 操作仅 可用作 Database 类方法,而不是 DML 语句。convertLead
- Database 类还提供了不作为 DML 语句提供的方法,例如 作为事务控制和回滚的方法,清空回收站,以及 与 SOQL 查询相关的方法。
作为原子事务的 DML 操作
DML 操作在事务中执行。所有 DML 操作 在事务中,要么成功完成,要么在一个操作中发生错误,则整个 事务将回滚,并且不会将任何数据提交到数据库。事务的边界 可以是触发器、类方法、匿名代码块、Apex 页面或自定义 Web 服务 方法。
在事务边界内发生的所有操作都表示一个操作单元。 这也适用于从事务边界对外部代码进行的调用,例如 由于在事务边界中运行的代码而触发的类或触发器。为 例如,请考虑以下操作链:自定义 Apex Web 服务方法调用方法 在执行某些 DML 操作的类中。在这种情况下,所有更改都将提交到 只有在事务中的所有操作完成执行后才使用数据库,并且不会导致任何错误。 如果在任何中间步骤中发生错误,则将回滚所有数据库更改,并且 事务未提交。
DML 操作
使用 DML,您可以插入新记录并将其提交到数据库。您还可以 更新现有记录的字段值。
- 插入和更新记录
使用 DML,您可以插入新记录并将其提交到数据库。同样,您可以更新现有记录的字段值。 - Upserting 提单记录
- 合并记录
- 删除记录
- 恢复已删除的记录
- 转换潜在客户
插入和更新记录
使用 DML,您可以插入新记录并将其提交到数据库。同样,你 可以更新现有记录的字段值。
重要
在可能的情况下,我们更改了非包容性条款,以符合我们的 平等的公司价值观。我们保留了某些条款,以避免对 客户实施。
本示例插入三条客户记录并更新现有客户记录。第一 创建三个帐户 sObject 并将其添加到列表中。插入语句批量插入 帐户列表作为参数。然后,更新第二个客户记录, 更新计费城市,并调用 update 语句以将更改保留在 数据库。
Account[] accts = new List<Account>();
for(Integer i=0;i<3;i++) {
Account a = new Account(Name='Acme' + i,
BillingCity='San Francisco');
accts.add(a);
}
Account accountToUpdate;
try {
insert accts;
// Update account Acme2.
accountToUpdate =
[SELECT BillingCity FROM Account
WHERE Name='Acme2' AND BillingCity='San Francisco'
LIMIT 1];
// Update the billing city.
accountToUpdate.BillingCity = 'New York';
// Make the update call.
update accountToUpdate;
} catch(DmlException e) {
System.debug('An unexpected error has occurred: ' + e.getMessage());
}
// Verify that the billing city was updated to New York.
Account afterUpdate =
[SELECT BillingCity FROM Account WHERE Id=:accountToUpdate.Id];
System.assertEquals('New York', afterUpdate.BillingCity);
插入相关记录
如果关系已经存在,则可以插入与现有记录相关的记录 在两个对象之间定义,例如查找或主从关系。一个 记录通过外键 ID 与相关记录相关联。例如 插入新联系人时,可以指定联系人的相关客户记录 通过设置字段的值。AccountId
本示例通过设置联系人的字段将联系人添加到客户(相关记录)中。联系和 帐户通过查找关系链接。AccountId
try {
Account acct = new Account(Name='SFDC Account');
insert acct;
// Once the account is inserted, the sObject will be
// populated with an ID.
// Get this ID.
ID acctID = acct.ID;
// Add a contact to this account.
Contact con = new Contact(
FirstName='Joe',
LastName='Smith',
Phone='415.555.1212',
AccountId=acctID);
insert con;
} catch(DmlException e) {
System.debug('An unexpected error has occurred: ' + e.getMessage());
}
更新相关记录
无法使用对 DML 操作的相同调用来更新相关记录上的字段 并且需要单独的 DML 调用。例如,如果插入新联系人,您可以 通过设置字段的值来指定联系人的相关客户记录。但是,您无法更改 帐户的名称,而不使用单独的 DML 调用更新帐户本身。 同样,在更新联系人时,如果您还想更新联系人的 相关帐户,您必须进行两次 DML 调用。以下示例更新了一个 使用两个语句的联系人及其相关帐户。
AccountIdupdate
try {
// Query for the contact, which has been associated with an account.
Contact queriedContact = [SELECT Account.Name
FROM Contact
WHERE FirstName = 'Joe' AND LastName='Smith'
LIMIT 1];
// Update the contact's phone number
queriedContact.Phone = '415.555.1213';
// Update the related account industry
queriedContact.Account.Industry = 'Technology';
// Make two separate calls
// 1. This call is to update the contact's phone.
update queriedContact;
// 2. This call is to update the related account's Industry field.
update queriedContact.Account;
} catch(Exception e) {
System.debug('An unexpected error has occurred: ' + e.getMessage());
}
- 使用外部 ID 关联记录 使用父记录上的自定义外部 ID
字段添加相关记录。通过外部 ID 字段关联记录是使用记录 ID 的替代方法。仅当已为所涉及的对象定义了关系(如主从-细节或查找)时,才能将相关记录添加到另一条记录中。 - 使用外键在单个语句中创建父记录和子记录
使用外部 ID 关联记录
使用父记录上的自定义外部 ID 字段添加相关记录。 通过外部 ID 字段关联记录是使用记录 ID 的替代方法。你 只有当关系(如主从-细节或 lookup) 已为所涉及的对象定义。
重要
在可能的情况下,我们更改了非包容性条款,以符合我们的 平等的公司价值观。我们保留了某些条款,以避免对 客户实施。此示例将新商机与现有客户关联。帐户 sObject 具有 标记为外部 ID 的自定义字段。商机记录与客户记录相关联 通过自定义外部 ID 字段。该示例假定:
- Account sObject 具有一个名为 MyExtID 的文本类型的外部 ID 字段
- 客户记录存在于以下位置MyExtID__c = ‘SAP111111’
在插入新商机之前,客户记录将作为 通过 Opportunity.Account 关系字段的 sObject。
Opportunity newOpportunity = new Opportunity(
Name='OpportunityWithAccountInsert',
StageName='Prospecting',
CloseDate=Date.today().addDays(7));
// Create the parent record reference.
// An account with external ID = 'SAP111111' already exists.
// This sObject is used only for foreign key reference
// and doesn't contain any other fields.
Account accountReference = new Account(
MyExtID__c='SAP111111');
// Add the account sObject to the opportunity.
newOpportunity.Account = accountReference;
// Create the opportunity.
Database.SaveResult results = Database.insert(newOpportunity);
前面的示例执行插入操作,但您也可以通过 执行更新或更新插入时的外部 ID 字段。如果父记录不存在,则 可以使用单独的 DML 语句或使用相同的 DML 语句创建它,如使用外键在单个语句中创建父记录和子记录中所示。
在单个语句中使用 外键
您可以使用外部 ID 字段作为外键来创建父记录和子记录 在单个步骤中执行不同的 sObject 类型,而不是创建父记录 首先,查询其 ID,然后创建子记录。为此,请执行以下操作:
- 创建子 sObject 并填充其必填字段,并根据需要 其他领域。
- 创建仅用于设置父外部的父引用 sObject 子 sObject 上的键引用。此 sObject 只有外部 ID 已定义字段,未设置其他字段。
- 将子 sObject 的外键字段设置为父引用 s您刚刚创建的对象。
- 创建另一个要传递给语句的父 sObject。此 sObject 必须具有 除了 外部 ID 字段。insert
- 通过向它传递一个数组来调用 要创建的 sObjects。父 sObject 必须位于 数组,即父数组的数组索引必须低于 儿童索引。insert
您可以创建最多 10 级的相关记录。此外,相关的 在单个调用中创建的记录必须具有不同的 sObject 类型。查看更多 信息,请参阅为不同对象创建记录 SOAP API 开发人员指南中的类型。以下示例说明如何使用父帐户创建商机 同样的陈述。示例 创建一个 Opportunity sObject 并填充其中的一些字段,然后创建两个 Account 对象。第一个帐户仅用于外键关系,而 第二个是用于帐户创建,并设置了帐户字段。两个帐户 设置外部 ID 字段 。 接下来,示例调用 向它传递一个 sObject 数组。数组中的第一个元素是父元素 sObject,第二个是机会 sObject。该语句创建 Opportunity 及其父帐户只需一步即可完成。最后,该示例检查 结果,并将所创建记录的 ID 写入调试日志,或第一个 如果记录创建失败,则出错。此示例需要 调用的帐户 MyExtID。
insertMyExtID__cDatabase.insertDatabase.insert
public class ParentChildSample {
public static void InsertParentChild() {
Date dt = Date.today();
dt = dt.addDays(7);
Opportunity newOpportunity = new Opportunity(
Name='OpportunityWithAccountInsert',
StageName='Prospecting',
CloseDate=dt);
// Create the parent reference.
// Used only for foreign key reference
// and doesn't contain any other fields.
Account accountReference = new Account(
MyExtID__c='SAP111111');
newOpportunity.Account = accountReference;
// Create the Account object to insert.
// Same as above but has Name field.
// Used for the insert.
Account parentAccount = new Account(
Name='Hallie',
MyExtID__c='SAP111111');
// Create the account and the opportunity.
Database.SaveResult[] results = Database.insert(new SObject[] {
parentAccount, newOpportunity });
// Check results.
for (Integer i = 0; i < results.size(); i++) {
if (results[i].isSuccess()) {
System.debug('Successfully created ID: '
+ results[i].getId());
} else {
System.debug('Error: could not create sobject '
+ 'for array element ' + i + '.');
System.debug(' The error reported was: '
+ results[i].getErrors()[0].getMessage() + '\n');
}
}
}
}
Upserting 提单记录
使用该操作,您可以 在一次通话中插入或更新现有记录。确定记录是否已 存在、语句或 Database 方法 使用记录的 ID 作为键来匹配记录、自定义外部 ID 字段或 idLookup 属性设置为 true 的标准字段。
upsertupsert
- 如果键不匹配,则创建新的对象记录。
- 如果键匹配一次,则更新现有对象记录。
- 如果键多次匹配,则会生成错误和对象 记录既不会插入也不会更新。
注意
仅当自定义字段具有唯一且将“ABC”和“abc”视为重复时,自定义字段匹配才不区分大小写 值(不区分大小写)属性被选为字段的一部分 定义。如果是这种情况,则“ABC123”与 “abc123。”有关详细信息,请参阅创建 自定义字段。
例子
以下示例更新位于 城市以前称为孟买,并且还插入了一个位于 San 的新帐户 弗朗西斯科:
Account[] acctsList = [SELECT Id, Name, BillingCity
FROM Account WHERE BillingCity = 'Bombay'];
for (Account a : acctsList) {
a.BillingCity = 'Mumbai';
}
Account newAcct = new Account(Name = 'Acme', BillingCity = 'San Francisco');
acctsList.add(newAcct);
try {
upsert acctsList;
} catch (DmlException e) {
// Process exception here
}
注意
有关处理的详细信息,请参阅批量 DML 异常处理。DmlException
下一个示例使用该方法更新插入传入的潜在顾客集合。此示例允许 记录的部分处理,即如果某些记录处理失败, 其余记录仍会插入或更新。它遍历结果和 将新任务添加到已成功处理的每条记录中。任务 sObjects 是 保存在列表中,然后批量插入。此示例后面跟着一个测试类 其中包含用于测试示例的测试方法。Database.upsert
/* This class demonstrates and tests the use of the
* partial processing DML operations */
public class DmlSamples {
/* This method accepts a collection of lead records and
creates a task for the owner(s) of any leads that were
created as new, that is, not updated as a result of the upsert
operation */
public static List<Database.upsertResult> upsertLeads(List<Lead> leads) {
/* Perform the upsert. In this case the unique identifier for the
insert or update decision is the Salesforce record ID. If the
record ID is null the row will be inserted, otherwise an update
will be attempted. */
List<Database.upsertResult> uResults = Database.upsert(leads,false);
/* This is the list for new tasks that will be inserted when new
leads are created. */
List<Task> tasks = new List<Task>();
for(Database.upsertResult result:uResults) {
if (result.isSuccess() && result.isCreated())
tasks.add(new Task(Subject = 'Follow-up', WhoId = result.getId()));
}
/* If there are tasks to be inserted, insert them */
Database.insert(tasks);
return uResults;
}
}
@isTest
private class DmlSamplesTest {
public static testMethod void testUpsertLeads() {
/* We only need to test the insert side of upsert */
List<Lead> leads = new List<Lead>();
/* Create a set of leads for testing */
for(Integer i = 0;i < 100; i++) {
leads.add(new Lead(LastName = 'testLead', Company = 'testCompany'));
}
/* Switch to the runtime limit context */
Test.startTest();
/* Exercise the method */
List<Database.upsertResult> results = DmlSamples.upsertLeads(leads);
/* Switch back to the test context for limits */
Test.stopTest();
/* ID set for asserting the tasks were created as expected */
Set<Id> ids = new Set<Id>();
/* Iterate over the results, asserting success and adding the new ID
to the set for use in the comprehensive assertion phase below. */
for(Database.upsertResult result:results) {
System.assert(result.isSuccess());
ids.add(result.getId());
}
/* Assert that exactly one task exists for each lead that was inserted. */
for(Lead l:[SELECT Id, (SELECT Subject FROM Tasks) FROM Lead WHERE Id IN :ids]) {
System.assertEquals(1,l.tasks.size());
}
}
}
使用外部 ID 可以减少 代码中 DML 语句的数量,并帮助您避免达到调控器限制 (见执行 调速器和限制)。下一个示例使用 Asset 对象上的外部 ID 字段来维护一对一关系 在资产和商机明细项之间。upsertupsertLine_Item_Id__c
注意
在运行此示例之前,请在名为 Asset 对象的对象上创建一个自定义文本字段,并将其标记为外部 同上。有关自定义字段的信息,请参阅 Salesforce 联机帮助。Line_Item_Id__c
public void upsertExample() {
Opportunity opp = [SELECT Id, Name, AccountId,
(SELECT Id, PricebookEntry.Product2Id, PricebookEntry.Name
FROM OpportunityLineItems)
FROM Opportunity
WHERE HasOpportunityLineItem = true
LIMIT 1];
Asset[] assets = new Asset[]{};
// Create an asset for each line item on the opportunity
for (OpportunityLineItem lineItem:opp.OpportunityLineItems) {
//This code populates the line item Id, AccountId, and Product2Id for each asset
Asset asset = new Asset(Name = lineItem.PricebookEntry.Name,
Line_Item_ID__c = lineItem.Id,
AccountId = opp.AccountId,
Product2Id = lineItem.PricebookEntry.Product2Id);
assets.add(asset);
}
try {
upsert assets Line_Item_ID__c; // This line upserts the assets list with
// the Line_Item_Id__c field specified as the
// Asset field that should be used for matching
// the record that should be upserted.
} catch (DmlException e) {
System.debug(e.getMessage());
}
}
合并记录
当您在 数据库,清理数据并合并记录可能会 是个好主意。您最多可以合并同一 sObject 的三条记录 类型。操作 最多将三条记录合并到其中一条记录中,删除其他记录, 并重新处理任何相关记录。merge
例
下面演示如何 将现有客户记录合并到主账户中。帐户 要合并,则具有相关联系人,该联系人将移至主帐户 合并操作后的记录。此外,合并后,合并记录 被删除,数据库中仅保留一条记录。这个例子 首先创建一个包含两个帐户的列表,然后插入该列表。然后 它执行查询以从数据库中获取新的客户记录, 并将联系人添加到要合并的客户中。接下来,它合并 两个帐户。最后,它验证联系人是否已移动 到主账户,第二个账户已被删除。
// Insert new accounts
List<Account> ls = new List<Account>{
new Account(name='Acme Inc.'),
new Account(name='Acme')
};
insert ls;
// Queries to get the inserted accounts
Account masterAcct = [SELECT Id, Name FROM Account WHERE Name = 'Acme Inc.' LIMIT 1];
Account mergeAcct = [SELECT Id, Name FROM Account WHERE Name = 'Acme' LIMIT 1];
// Add a contact to the account to be merged
Contact c = new Contact(FirstName='Joe',LastName='Merged');
c.AccountId = mergeAcct.Id;
insert c;
try {
merge masterAcct mergeAcct;
} catch (DmlException e) {
// Process exception
System.debug('An unexpected error has occurred: ' + e.getMessage());
}
// Once the account is merged with the master account,
// the related contact should be moved to the master record.
masterAcct = [SELECT Id, Name, (SELECT FirstName,LastName From Contacts)
FROM Account WHERE Name = 'Acme Inc.' LIMIT 1];
System.assert(masterAcct.getSObjects('Contacts').size() > 0);
System.assertEquals('Joe', masterAcct.getSObjects('Contacts')[0].get('FirstName'));
System.assertEquals('Merged', masterAcct.getSObjects('Contacts')[0].get('LastName'));
// Verify that the merge record got deleted
Account[] result = [SELECT Id, Name FROM Account WHERE Id=:mergeAcct.Id];
System.assertEquals(0, result.size());
第二个例子 与前一个类似,只是它使用方法(而不是 声明)。这 的最后一个参数设置为 此操作中遇到的任何错误都会在合并结果中返回 而不是获得异常。该示例将两个帐户合并到 主帐户并检索返回的结果。示例 创建一个主帐户和两个副本,其中一个具有子帐户 联系。它验证在合并后联系人是否移动到 主帐户。Database.mergemergeDatabase.mergefalse
// Create master account
Account master = new Account(Name='Account1');
insert master;
// Create duplicate accounts
Account[] duplicates = new Account[]{
// Duplicate account
new Account(Name='Account1, Inc.'),
// Second duplicate account
new Account(Name='Account 1')
};
insert duplicates;
// Create child contact and associate it with first account
Contact c = new Contact(firstname='Joe',lastname='Smith', accountId=duplicates[0].Id);
insert c;
// Get the account contact relation ID, which is created when a contact is created on "Account1, Inc."
AccountContactRelation resultAcrel = [SELECT Id FROM AccountContactRelation WHERE ContactId=:c.Id LIMIT 1];
// Merge accounts into master
Database.MergeResult[] results = Database.merge(master, duplicates, false);
for(Database.MergeResult res : results) {
if (res.isSuccess()) {
// Get the master ID from the result and validate it
System.debug('Master record ID: ' + res.getId());
System.assertEquals(master.Id, res.getId());
// Get the IDs of the merged records and display them
List<Id> mergedIds = res.getMergedRecordIds();
System.debug('IDs of merged records: ' + mergedIds);
// Get the ID of the reparented record and
// validate that this the contact ID.
System.debug('Reparented record ID: ' + res.getUpdatedRelatedIds());
// Make sure there are two IDs (contact ID and account contact relation ID); the order isn't defined
System.assertEquals(2, res.getUpdatedRelatedIds().size() );
boolean flag1 = false;
boolean flag2 = false;
// Because the order of the IDs isn't defined, the ID can be at index 0 or 1 of the array
if (resultAcrel.id == res.getUpdatedRelatedIds()[0] || resultAcrel.id == res.getUpdatedRelatedIds()[1] )
flag1 = true;
if (c.id == res.getUpdatedRelatedIds()[0] || c.id == res.getUpdatedRelatedIds()[1] )
flag2 = true;
System.assertEquals(flag1, true);
System.assertEquals(flag2, true);
}
else {
for(Database.Error err : res.getErrors()) {
// Write each error to the debug output
System.debug(err.getMessage());
}
}
}
合并注意事项
合并 sObject 时 记录中,请考虑以下规则和准则:
- 只能合并潜在顾客、联系人、案例和客户。请参阅不支持 DML 操作的 sObject。
- 您可以传递一个主记录和最多两个附加的 sObject 记录到单个方法。merge
- 使用 Apex 合并操作时,主记录上的字段值始终取代 要合并的记录上的相应字段值。要保留合并的 记录字段值,只需在主 sObject 上设置此字段值即可 执行合并。
- 外部 ID 字段不能与 一起使用。merge
删除记录
在数据库中保留记录后,可以使用该操作删除这些记录。已删除的记录不会被删除 永久来自 Salesforce,但它们将被放置在回收站中 15 天,从 它们可以恢复的地方。恢复已删除的记录将在后面的部分中介绍。delete
例
以下示例删除名为“DotCom”的所有帐户:
Account[] doomedAccts = [SELECT Id, Name FROM Account
WHERE Name = 'DotCom'];
try {
delete doomedAccts;
} catch (DmlException e) {
// Process exception here
}
注意
有关处理的详细信息,请参阅批量 DML 异常处理。DmlException
参考 删除和还原记录时的完整性
该操作支持级联删除。如果 删除父对象时,会自动删除其子对象,只要每个子对象 可以删除记录。
delete
例如,如果您删除案例记录,则 Apex 会自动 删除与之关联的任何 CaseComment、CaseHistory 和 CaseSolution 记录 箱。但是,如果特定子记录不可删除或当前正在删除 used,则对 父案例记录失败。delete该操作还原以下类型关系的记录关联:
undelete
- 父帐户(如“父帐户”字段中指定) 在帐户上)
- 间接客户联系人关系(如“相关客户”中指定) 联系人上的相关列表或联系人上的相关联系人相关列表 帐户)
- 父案例(如 案例)
- 已翻译解决方案的主解决方案(如主解决方案中指定的那样) 解决方案上的解决方案字段)
- 联系人的经理(如“报告对象”字段中指定) 在联系人上)
- 与资产相关的产品(在资产的“产品”字段中指定)
- 与报价单相关的商机(在报价单的 Opportunity 字段中指定)
- 所有自定义查找关系
- 帐户和关系组上的关系组成员,以及一些 异常
- 标签
- 文章的类别、发布状态和作业
注意
Salesforce的 仅恢复尚未替换的查找关系。例如,如果 在原始产品记录之前,资产与不同的产品相关 如果未删除,则不会恢复该资产-产品关系。
恢复已删除的记录
删除记录后,这些记录将放置在回收站中 15 天, 之后,它们将被永久删除。当记录仍在回收中时 Bin,您可以使用该操作恢复它们。如果您不小心删除了某些要保留的记录,请还原它们 从回收站。undelete
例
以下示例取消删除名为“Universal Containers”的帐户。关键字查询顶部的所有行 级别和聚合关系,包括已删除的记录和存档的记录 活动。
ALL ROWS
Account a = new Account(Name='Universal Containers');
insert(a);
insert(new Contact(LastName='Carter',AccountId=a.Id));
delete a;
Account[] savedAccts = [SELECT Id, Name FROM Account WHERE Name = 'Universal Containers' ALL ROWS];
try {
undelete savedAccts;
} catch (DmlException e) {
// Process exception here
}
注意
有关处理的详细信息,请参阅批量 DML 异常处理。DmlException
取消删除注意事项
使用该语句时,请注意以下事项。
undelete
- 您可以取消删除因合并而删除的记录。 但是,合并会重新设置子对象的父级,而该重属不能 被撤消。
- 标识已删除的记录,包括由于 合并,使用参数 替换为 SOQL 查询。ALL ROWS
- 请参阅删除和恢复记录时的参照完整性。
转换潜在客户
DML 操作 将潜在顾客转换为客户和联系人,以及(可选) 机会。convertLead convertLead仅作为 班级;它不能作为 DML 使用 陈述。Database
转换潜在客户涉及以下基本步骤:
- 您的应用程序确定要转换的任何潜在顾客的 ID。
- (可选)应用程序确定要进入的任何帐户的 ID 合并潜在客户。应用程序可以使用 SOQL 搜索与 潜在顾客名称,如以下示例所示:
SELECT Id, Name FROM Account WHERE Name='CompanyNameOfLeadBeingMerged'
- (可选)应用程序将一个或多个联系人的 ID 确定为 合并潜在客户。应用程序可以使用 SOQL 搜索 匹配主要联系人姓名,如下所示 例:
SELECT Id, Name FROM Contact WHERE FirstName='FirstName' AND LastName='LastName' AND AccountId = '001...'
- (可选)应用程序确定是否应从 线索。
- 应用程序使用查询 () 来获取已转换的潜在客户 地位。SELECT … FROM LeadStatus WHERE IsConverted=true
- 应用程序调用 。convertLead
- 应用程序循环访问返回的一个或多个结果,并检查每个结果 LeadConvertResult 对象,用于确定每个 铅。
- (可选)在转换队列拥有的潜在顾客时,必须指定所有者。 这是因为客户和联系人不能归队列所有。即使你是 指定现有客户或联系人时,仍必须指定所有者。
例
此示例演示如何使用该方法转换潜在顾客。它插入了一条新的引线, 创建一个对象,设置其 status 设置为 converted,然后将其传递给该方法。最后,它验证转换 成功了。Database.convertLeadLeadConvertDatabase.convertLead
Lead myLead = new Lead(LastName = 'Fry', Company='Fry And Sons');
insert myLead;
Database.LeadConvert lc = new database.LeadConvert();
lc.setLeadId(myLead.id);
LeadStatus convertStatus = [SELECT Id, MasterLabel FROM LeadStatus WHERE IsConverted=true LIMIT 1];
lc.setConvertedStatus(convertStatus.MasterLabel);
Database.LeadConvertResult lcr = Database.convertLead(lc);
System.assert(lcr.isSuccess());
转换潜在客户注意事项
- 字段映射:系统自动将标准潜在客户字段映射到标准 客户、联系人和商机字段。对于自定义潜在客户字段,您的 Salesforce 管理员可以指定它们如何映射到自定义帐户、联系人、 和机会字段。有关字段映射的更多信息,请参阅 Salesforce 帮助。
- 合并字段:如果数据合并到现有客户和联系人对象中,则仅 目标对象中的空字段将被覆盖 – 现有数据 (包括 ID)不会被覆盖。唯一的例外是,如果您在 LeadConvert 对象设置为 true,在这种情况下,目标联系人对象中的字段将覆盖 字段中的内容 源 LeadConvert 对象。setOverwriteLeadSourceLeadSourceLeadSource
- 记录类型:如果组织使用记录类型,则默认记录类型为 新所有者将分配给潜在顾客转换期间创建的记录。默认 转换潜在顾客的用户的记录类型决定了潜在顾客来源值 在转换期间可用。如果所需的潜在客户源值不是 available,将值添加到用户转换 铅。有关记录类型的更多信息,请参阅 Salesforce 帮助。
- 选择列表值:系统为帐户分配默认选择列表值, 联系人和机会,在映射任何标准潜在客户选择列表字段时 空白。如果您的组织使用记录类型,则空白值将替换为 新记录所有者的默认选择列表值。
- 自动订阅 Feed:当您将潜在客户转化为新帐号时, 联系人和商机,潜在客户所有者已取消订阅潜在客户记录的 聊天提要。主要所有者、生成记录的所有者以及 已订阅的潜在客户不会自动订阅 生成的记录,除非它们在 Chatter 摘要设置。他们必须启用自动订阅才能查看 更改其新闻源中的客户、联系人和商机记录。自 订阅他们创建的记录,用户必须启用自动 遵循我在他们的个人设置中创建选项的记录。一个 用户可以订阅记录,以便对记录的更改显示在新闻中 用户主页上的源。这是了解最新情况的有用方法 对 Salesforce 中记录的更改。
异常处理
如果出现问题,DML 语句将返回运行时异常 在执行 DML 操作期间在数据库中。您可以 通过包装 DML 语句来处理代码中的异常 在 try-catch 块中。以下示例在 try-catch 中包含 DML 语句 块。insert
Account a = new Account(Name='Acme');
try {
insert a;
} catch(DmlException e) {
// Process exception here
}
Database 类方法 Result 对象
数据库类方法返回数据操作的结果。 这些结果对象包含有关数据操作的有用信息 对于每条记录,例如操作是否成功, 以及任何错误信息。每种类型的操作都返回一个特定的 result 对象类型,如下所述。
操作 | Result 类 |
---|---|
插入、更新 | SaveResult 类 |
更新插入 | UpsertResult 类 |
合并 | MergeResult 类 |
删除 | DeleteResult 类 |
取消删除 | UndeleteResult 类 |
convertLead(转换铅) | LeadConvertResult 类 |
emptyRecycleBin | EmptyRecycleBinResult 类 |
返回的数据库错误
而 DML 语句总是在操作时返回异常 正在处理的其中一条记录失败,操作是 回滚所有记录,Database 类方法可以执行 因此,或允许记录处理部分成功。在后一种情况下 部分处理,Database 类方法不会抛出异常。 相反,它们会返回发生的任何错误的错误列表 在失败的记录上。
这些错误提供有关失败的详细信息,并包含在内 在 Database 类方法的结果中。例如,返回一个对象 插入和更新操作。与所有返回的结果一样,包含一个调用的方法,该方法返回对象列表,表示 遇到的错误(如果有)。SaveResultSaveResultgetErrorsDatabase.Error
例
此示例演示如何获取 操作返回的错误。它插入两个帐户,其中一个没有 具有必填的 Name 字段,并将第二个参数设置为 : 。这将设置部分处理 选择。接下来,该示例检查调用是否有任何失败,然后迭代 通过错误,将错误信息写入调试日志。Database.insertfalseDatabase.insert(accts, false);if (!sr.isSuccess())
// Create two accounts, one of which is missing a required field
Account[] accts = new List<Account>{
new Account(Name='Account1'),
new Account()};
Database.SaveResult[] srList = Database.insert(accts, false);
// Iterate through each returned result
for (Database.SaveResult sr : srList) {
if (!sr.isSuccess()) {
// Operation failed, so get all errors
for(Database.Error err : sr.getErrors()) {
System.debug('The following error has occurred.');
System.debug(err.getStatusCode() + ': ' + err.getMessage());
System.debug('Fields that affected this error: ' + err.getFields());
}
}
}