Dynamic Apex 通过为开发人员提供 能够:
- 访问 sObject 和字段描述信息描述信息提供有关 sObject 和字段属性的元数据信息。 例如,sObject 的描述信息包括 该类型的 sObject 支持创建或取消删除等操作, sObject 的名称和标签、sObject 的字段和子对象, 等等。字段的描述信息包括 字段有默认值,是否为计算字段,类型 的领域,依此类推。请注意,描述信息提供 有关组织中对象的信息,而不是个人的信息 记录。
- 访问 Salesforce 应用程序信息您可以获取以下描述信息 Salesforce 用户界面中提供的标准和自定义应用程序。每个应用对应一个 选项卡的集合。描述应用的信息包括应用的标签、命名空间和选项卡。 选项卡的描述信息包括与选项卡关联的 sObject、选项卡图标和 颜色。
- 编写动态 SOQL 查询、动态 SOSL 查询和动态 DML动态 SOQL 和 SOSL 查询提供了将 SOQL 或 SOSL 作为 字符串,而动态 DML 提供了创建记录的能力 动态,然后使用 DML 将其插入数据库。使用动态 SOQL、SOSL 和 DML, 应用程序可以精确地针对组织以及用户的 权限。 这对于从以下位置安装的应用程序非常有用 AppExchange。
- 了解 Apex Describe 信息
- 使用字段令牌
- 了解描述信息权限
- 使用 Schema 方法描述 sObjects
- 使用架构方法描述选项卡
- 访问所有 sObject
- 访问与 sObject 关联的所有数据类别
- 动态 SOQL
- 动态 SOSL
- 动态 DML
了解 Apex Describe 信息
可以使用标记或 Schema 方法描述 sObjects。describeSObjects
Apex 提供了两种数据结构和一种用于 sObject 和字段描述信息的方法:
- 令牌 – 对 sObject 或字段的轻量级、可序列化引用 在编译时进行验证。这用于令牌描述。
- 方法 – 类中对一个或多个执行描述的方法 sObject 类型。describeSObjectsSchema
- Describe result – 包含 s对象或字段。Describe 结果对象不可序列化,在运行时进行验证。 在执行描述时,将返回此结果对象,使用 sObject 标记或 方法。Schema.DescribeSObjectResultdescribeSObjects
使用令牌描述 sObject
这很容易 从标记移动到其描述结果,反之亦然。sObject 和字段标记都具有 返回描述结果的方法 对于该令牌。在描述结果中,和方法 分别返回 sObject 和 field 的标记。getDescribegetSObjectTypegetSObjectField
因为令牌是轻量级的, 使用它们可以使您的代码更快、更高效。例如,使用 sObject 或字段,当您确定代码的 sObject 或字段的类型时 需要使用。可以使用相等运算符 () 比较令牌,以确定 sObject 是否为 Account 对象,因为 example,或者字段是 Name 字段还是自定义计算字段。==
以下代码提供了如何使用令牌和描述结果的一般示例 访问有关 sObject 和字段的信息 性能:
// Create a new account as the generic type sObject
sObject s = new Account();
// Verify that the generic sObject is an Account sObject
System.assert(s.getsObjectType() == Account.sObjectType);
// Get the sObject describe result for the Account object
Schema.DescribeSObjectResult dsr = Account.sObjectType.getDescribe();
// Get the field describe result for the Name field on the Account object
Schema.DescribeFieldResult dfr = Schema.sObjectType.Account.fields.Name;
// Verify that the field token is the token for the Name field on an Account object
System.assert(dfr.getSObjectField() == Account.Name);
// Get the field describe result from the token
dfr = dfr.getSObjectField().getDescribe();
以下算法显示了您如何 可以在 Apex 中使用描述信息:
- 为组织中的 sObject 生成令牌列表或映射(请参阅访问所有 sObject。
- 确定需要访问的 sObject。
- 生成 sObject 的描述结果。
- 如有必要,请为 sObject 生成字段标记的映射(请参阅访问所有字段描述结果以获取 sObject。
- 为代码需要访问的字段生成描述结果。
使用 sObject 令 牌
SObjects,如 Account 和 MyCustomObject__c,充当具有特殊 static 的静态类 用于访问令牌和描述结果信息的方法和成员变量。您必须 在编译时显式引用 sObject 和字段名称以获取对 describe 的访问权限 结果。
若要访问 sObject 的令牌,请使用下列方法之一:
- 访问 sObject 上的成员变量 类型,例如 Account。sObjectType
- 在 sObject 上调用该方法 describe result、sObject 变量、列表或映射。getSObjectType
Schema.SObjectType是 sObject 的数据类型 令 牌。
在以下示例中,返回 Account sObject 的令牌:
Schema.sObjectType t = Account.sObjectType;
以下命令还返回帐户 sObject 的令牌:
Account a = new Account();
Schema.sObjectType t = a.getSObjectType();
此示例可用于确定 sObject 或 sObject 列表是否属于 特殊类型:
// Create a generic sObject variable s
SObject s = Database.query('SELECT Id FROM Account LIMIT 1');
// Verify if that sObject variable is an Account token
System.assertEquals(s.getSObjectType(), Account.sObjectType);
// Create a list of generic sObjects
List<sObject> sobjList = new Account[]{};
// Verify if the list of sObjects contains Account tokens
System.assertEquals(sobjList.getSObjectType(), Account.sObjectType);
某些标准 sObject 具有一个名为 的字段,例如 AssignmentRule、QueueSObject 和 RecordType。对于这些类型 的 sObjects,请始终使用 检索令牌。例如,如果使用该属性,则返回该字段。sObjectTypegetSObjectTypeRecordType.sObjectType
使用 获取 sObject 描述结果 令 牌
要访问 sObject,请使用以下方法之一:
- 在 sObject 上调用该方法 令 牌。getDescribe
- 将 Schema 静态变量与 sObject 的名称。例如。sObjectTypeSchema.sObjectType.Lead
Schema.DescribeSObjectResult是数据 type 作为 sObject 描述结果。
下面的示例对 sObject 使用该方法 令 牌:getDescribe
Schema.DescribeSObjectResult dsr = Account.sObjectType.getDescribe();
这 以下示例使用 Schema static 成员 变量:
sObjectType
Schema.DescribeSObjectResult dsr = Schema.SObjectType.Account;
为 有关 sObject 描述结果可用的方法的更多信息,请参阅 DescribeSObjectResultClass。
使用字段令牌
若要访问字段的令牌,请使用下列方法之一:
- 访问 sObject 静态类型的静态成员变量名称,例如 .Account.Name
- 在字段上调用方法 描述结果。getSObjectField
字段 token 使用数据类型 。Schema.SObjectField
在以下示例中,为 Account 对象的字段返回字段 token:Description
Schema.SObjectField fieldToken = Account.Description;
在以下示例中,字段 token 是从字段 describe result 返回的:
// Get the describe result for the Name field on the Account object
Schema.DescribeFieldResult dfr = Schema.sObjectType.Account.fields.Name;
// Verify that the field token is the token for the Name field on an Account object
System.assert(dfr.getSObjectField() == Account.Name);
// Get the describe result from the token
dfr = dfr.getSObjectField().getDescribe();
注意
字段令牌不适用于个人帐户。如果你 访问 出现异常错误。相反,将字段名称指定为字符串。Schema.Account.fieldname
使用字段描述 结果
要访问 字段中,请使用以下方法之一:
- 在字段上调用方法 令 牌。getDescribe
- 访问 sObject 的成员变量 具有字段成员变量(如 、 等)的令牌。fieldsNameBillingCity
字段 describe result 使用数据类型 。Schema.DescribeFieldResult
下面的示例使用该方法:getDescribe
Schema.DescribeFieldResult dfr = Account.Description.getDescribe();
这 示例使用 Member 变量方法:fields
Schema.DescribeFieldResult dfr = Schema.SObjectType.Account.fields.Name;
在上面的示例中,系统使用特殊解析来验证 最终成员变量 () 对 在编译时指定的 sObject。当解析器找到成员变量时,它会向后查找 sObject 的名称 ().它验证字段名称 跟在 Member 变量后面是合法的。 成员变量仅在 这种方式。NamefieldsAccountfieldsfields
注意
不要使用成员 变量,而不使用字段成员变量名称或方法。有关详细信息,请参阅下一节。fieldsgetMapgetMap
查看更多 有关字段 describe result 可用的方法的信息,请参阅 DescribeFieldResultClass。
访问所有字段描述结果 对于 sObject
使用字段 describe result 的方法返回一个映射,该映射表示 所有字段名称(键)和字段标记(值)之间的关系 s对象。getMap
以下示例生成一个映射,该映射可用于通过以下方式访问字段 名字:
Map<String, Schema.SObjectField> fieldMap = Schema.SObjectType.Account.fields.getMap();
注意
这 此映射的值类型不是字段描述结果。使用描述结果需要 系统资源过多。相反,它是一个令牌映射,您可以使用它来查找 适当的字段。确定字段后,生成 它。
该地图具有以下特征:
- 它是动态的,也就是说,它是在运行时在该 sObject 的字段上生成的。
- 所有字段名称均不区分大小写。
- 密钥根据需要使用命名空间。
- 这些键反映字段是否为自定义对象。
字段描述注意事项
描述字段时,请注意以下事项。
- 从已安装的托管包中执行的字段描述将返回 即使未在安装组织中启用 Chatter,也会使用 Chatter 字段。这不是 如果字段 describe 是从不在已安装的托管类中执行的,则为 true 包。
- 当您从 在 Apex 类中,无论 保存类的 API 版本。如果字段类型,例如地理位置字段 type,仅在最新的 API 版本中可用,地理位置字段的组件是 即使该类保存在早期的 API 版本中,也返回。
版本化行为更改
在 API 版本 34.0 及更高版本中,自定义 SObjectType 上的 Schema.DescribeSObjectResult 包括以命名空间为前缀的映射键,即使命名空间是当前命名空间的命名空间 执行代码。如果您使用多个命名空间并生成运行时描述数据, 请确保代码使用命名空间前缀正确访问密钥。
了解描述信息权限
Apex 类和触发器在系统模式下运行。类和触发器对 动态查找组织中可用的任何 sObject。您可以生成所有 组织的 sObjects,而不考虑当前用户的权限,除非您正在执行 匿名顶点。
当您在匿名块中执行描述调用时,用户权限很重要。因此, 如果对正在运行的用户的访问受到限制,则并非所有 sObject 和字段都可以查找。为 例如,如果您在匿名区块中描述帐户字段,并且您无权访问所有字段 字段,但并非所有字段都会返回。但是,对于同一调用,将返回所有字段 顶点类。
有关详细信息,请参阅“关于包中的 API 和动态顶点访问” Salesforce 帮助。
使用 Schema 方法描述 sObjects
作为使用令牌的替代方法,可以通过以下方式描述 sObjects 调用 Schema 方法并传递一个或多个 sObject 类型名称 s要描述的对象。describeSObjects
此示例获取两个 sObject 的描述元数据信息 types – Account 标准对象和自定义Merchandise__c对象 对象。获取每个 sObject 的描述结果后,这 example 将返回的信息写入调试输出,例如 作为 sObject 标签,字段数,是否为自定义对象 或不,以及子关系的数量。
// sObject types to describe
String[] types = new String[]{'Account','Merchandise__c'};
// Make the describe call
Schema.DescribeSobjectResult[] results = Schema.describeSObjects(types);
System.debug('Got describe information for ' + results.size() + ' sObjects.');
// For each returned result, get some info
for(Schema.DescribeSobjectResult res : results) {
System.debug('sObject Label: ' + res.getLabel());
System.debug('Number of fields: ' + res.fields.getMap().size());
System.debug(res.isCustom() ? 'This is a custom object.' : 'This is a standard object.');
// Get child relationships
Schema.ChildRelationship[] rels = res.getChildRelationships();
if (rels.size() > 0) {
System.debug(res.getName() + ' has ' + rels.size() + ' child relationships.');
}
}
使用架构方法描述选项卡
您可以获取有关应用程序及其选项卡的元数据信息 Salesforce 用户界面,通过在 Apex 中执行描述调用。此外,您可以获得更多 有关每个选项卡的详细信息。分别使用 Schema 方法和 中的方法。describeTabsgetTabsSchema.DescribeTabResult
此示例演示如何获取每个应用的选项卡集。然后,该示例获取 tab 描述 Sales 应用的元数据信息。对于每个选项卡,元数据信息 包括图标 URL、选项卡是否自定义以及颜色等。这 选项卡描述信息将写入调试输出。
// Get tab set describes for each app
List<Schema.DescribeTabSetResult> tabSetDesc = Schema.describeTabs();
// Iterate through each tab set describe for each app and display the info
for(DescribeTabSetResult tsr : tabSetDesc) {
String appLabel = tsr.getLabel();
System.debug('Label: ' + appLabel);
System.debug('Logo URL: ' + tsr.getLogoUrl());
System.debug('isSelected: ' + tsr.isSelected());
String ns = tsr.getNamespace();
if (ns == '') {
System.debug('The ' + appLabel + ' app has no namespace defined.');
}
else {
System.debug('Namespace: ' + ns);
}
// Display tab info for the Sales app
if (appLabel == 'Sales') {
List<Schema.DescribeTabResult> tabDesc = tsr.getTabs();
System.debug('-- Tab information for the Sales app --');
for(Schema.DescribeTabResult tr : tabDesc) {
System.debug('getLabel: ' + tr.getLabel());
System.debug('getColors: ' + tr.getColors());
System.debug('getIconUrl: ' + tr.getIconUrl());
System.debug('getIcons: ' + tr.getIcons());
System.debug('getMiniIconUrl: ' + tr.getMiniIconUrl());
System.debug('getSobjectName: ' + tr.getSobjectName());
System.debug('getUrl: ' + tr.getUrl());
System.debug('isCustom: ' + tr.isCustom());
}
}
}
// Example debug statement output
// DEBUG|Label: Sales
// DEBUG|Logo URL: https://MyDomainName.my.salesforce.com/img/seasonLogos/2014_winter_aloha.png
// DEBUG|isSelected: true
// DEBUG|The Sales app has no namespace defined.// DEBUG|-- Tab information for the Sales app --
// (This is an example debug output for the Accounts tab.)
// DEBUG|getLabel: Accounts
// DEBUG|getColors: (Schema.DescribeColorResult[getColor=236FBD;getContext=primary;getTheme=theme4;],
// Schema.DescribeColorResult[getColor=236FBD;getContext=primary;getTheme=theme3;],
// Schema.DescribeColorResult[getColor=236FBD;getContext=primary;getTheme=theme2;])
// DEBUG|getIconUrl: https://MyDomainName.my.salesforce.com/img/icon/accounts32.png
// DEBUG|getIcons: (Schema.DescribeIconResult[getContentType=image/png;getHeight=32;getTheme=theme3;
// getUrl=https://MyDomainName.my.salesforce.com/img/icon/accounts32.png;getWidth=32;],
// Schema.DescribeIconResult[getContentType=image/png;getHeight=16;getTheme=theme3;
// getUrl=https://MyDomainName.my.salesforce.com/img/icon/accounts16.png;getWidth=16;])
// DEBUG|getMiniIconUrl: https://MyDomainName.my.salesforce.com/img/icon/accounts16.png
// DEBUG|getSobjectName: Account
// DEBUG|getUrl: https://MyDomainName.my.salesforce.com/001/o
// DEBUG|isCustom: false
访问所有 sObject
使用 Schema 方法返回一个映射,该映射表示 所有 sObject 名称(键)到 sObject 令牌(值)。例如:getGlobalDescribe
Map<String, Schema.SObjectType> gd = Schema.getGlobalDescribe();
该地图具有以下特征:
- 它是动态的,也就是说,它是在运行时在 sObject 上生成的 当前可用于组织,具体取决于权限。
- sObject 名称不区分大小写。
- 键以命名空间为前缀(如果有)。*
- 这些键反映 sObject 是否为自定义对象。
*从使用 Salesforce API 版本 28.0 保存的 Apex 开始,映射中返回的键始终以 命名空间(如果有)的运行代码。例如,如果进行调用的代码块位于命名空间 NS1 中,并且自定义 名为 MyObject__c 的对象位于同一命名空间中,则返回的键为 。对于使用早期 API 版本保存的 Apex, 仅当代码块的命名空间和 sObject 的命名空间时,key 才包含命名空间 是不同的。例如,如果生成映射的代码块位于命名空间 N1 中,并且 sObject 也在 N1 中,映射中的键表示为 。但是,如果代码块位于命名空间 N1 中,并且 sObject 位于 命名空间 N2,键为 .getGlobalDescribegetGlobalDescribeNS1__MyObject__cMyObject__cN2__MyObject__c
标准 sObject 没有命名空间前缀。
注意
如果从已安装的托管包调用该方法,它将返回 sObject Chatter sObject 的名称和标记,例如 NewsFeed 和 UserProfileFeed, 即使未在安装组织中启用 Chatter。这 如果该方法是从不在已安装的托管包中的类调用的,则不为 true。getGlobalDescribegetGlobalDescribe
访问关联的所有数据类别 使用 sObject
使用 和 方法返回与特定对象关联的类别:describeDataCategoryGroupsdescribeDataCategoryGroupStructures
- 返回与所选对象关联的所有类别组(请参见)。describeDataCategoryGroups(sObjectNames)
- 从返回的映射中,获取要进一步扩展的类别组名称和 sObject 名称 询问(请参阅 DescribeDataCategoryGroupResult 类)。
- 指定类别组和关联的对象,然后检索 此对象可用的类别(请参见)。describeDataCategoryGroupStructures
该方法返回 可用于您指定的类别组中的对象的类别。对于其他 有关数据类别的信息,请参阅 Salesforce Online 中的“使用数据类别” 帮助。describeDataCategoryGroupStructures
在以下示例中,该方法返回所有类别组 与 Article 和 Question 对象相关联。该方法返回所有类别 可用于“区域”类别组中的文章和问题。对于其他 有关文章和问题的信息,请参阅 Salesforce 联机帮助。describeDataCategoryGroupSampledescribeDataCategoryGroupStructures
若要使用以下示例,必须:
- 启用 Salesforce Knowledge。
- 启用答案功能。
- 创建名为“区域”的数据类别组。
- 将 Regions 指定为 Answers 要使用的数据类别组。
- 确保将“区域”数据类别组分配给 Salesforce Knowledge。
有关创建数据类别组的详细信息,请参阅“创建和修改类别组”中的 Salesforce 联机帮助。有关答案的更多信息,请参阅 Salesforce 联机帮助。
public class DescribeDataCategoryGroupSample {
public static List<DescribeDataCategoryGroupResult> describeDataCategoryGroupSample(){
List<DescribeDataCategoryGroupResult> describeCategoryResult;
try {
//Creating the list of sobjects to use for the describe
//call
List<String> objType = new List<String>();
objType.add('KnowledgeArticleVersion');
objType.add('Question');
//Describe Call
describeCategoryResult = Schema.describeDataCategoryGroups(objType);
//Using the results and retrieving the information
for(DescribeDataCategoryGroupResult singleResult : describeCategoryResult){
//Getting the name of the category
singleResult.getName();
//Getting the name of label
singleResult.getLabel();
//Getting description
singleResult.getDescription();
//Getting the sobject
singleResult.getSobject();
}
} catch(Exception e){
}
return describeCategoryResult;
}
}
public class DescribeDataCategoryGroupStructures {
public static List<DescribeDataCategoryGroupStructureResult>
getDescribeDataCategoryGroupStructureResults(){
List<DescribeDataCategoryGroupResult> describeCategoryResult;
List<DescribeDataCategoryGroupStructureResult> describeCategoryStructureResult;
try {
//Making the call to the describeDataCategoryGroups to
//get the list of category groups associated
List<String> objType = new List<String>();
objType.add('KnowledgeArticleVersion');
objType.add('Question');
describeCategoryResult = Schema.describeDataCategoryGroups(objType);
//Creating a list of pair objects to use as a parameter
//for the describe call
List<DataCategoryGroupSobjectTypePair> pairs =
new List<DataCategoryGroupSobjectTypePair>();
//Looping throught the first describe result to create
//the list of pairs for the second describe call
for(DescribeDataCategoryGroupResult singleResult :
describeCategoryResult){
DataCategoryGroupSobjectTypePair p =
new DataCategoryGroupSobjectTypePair();
p.setSobject(singleResult.getSobject());
p.setDataCategoryGroupName(singleResult.getName());
pairs.add(p);
}
//describeDataCategoryGroupStructures()
describeCategoryStructureResult =
Schema.describeDataCategoryGroupStructures(pairs, false);
//Getting data from the result
for(DescribeDataCategoryGroupStructureResult singleResult : describeCategoryStructureResult){
//Get name of the associated Sobject
singleResult.getSobject();
//Get the name of the data category group
singleResult.getName();
//Get the name of the data category group
singleResult.getLabel();
//Get the description of the data category group
singleResult.getDescription();
//Get the top level categories
DataCategory [] toplevelCategories =
singleResult.getTopCategories();
//Recursively get all the categories
List<DataCategory> allCategories =
getAllCategories(toplevelCategories);
for(DataCategory category : allCategories) {
//Get the name of the category
category.getName();
//Get the label of the category
category.getLabel();
//Get the list of sub categories in the category
DataCategory [] childCategories =
category.getChildCategories();
}
}
} catch (Exception e){
}
return describeCategoryStructureResult;
}
private static DataCategory[] getAllCategories(DataCategory [] categories){
if(categories.isEmpty()){
return new DataCategory[]{};
} else {
DataCategory [] categoriesClone = categories.clone();
DataCategory category = categoriesClone[0];
DataCategory[] allCategories = new DataCategory[]{category};
categoriesClone.remove(0);
categoriesClone.addAll(category.getChildCategories());
allCategories.addAll(getAllCategories(categoriesClone));
return allCategories;
}
}
}
测试访问权限 到与 sObject 关联的所有数据类别
下面的示例测试前面所示的方法。它确保返回的类别组 和关联的对象是正确的。describeDataCategoryGroupSample
@isTest
private class DescribeDataCategoryGroupSampleTest {
public static testMethod void describeDataCategoryGroupSampleTest(){
List<DescribeDataCategoryGroupResult>describeResult =
DescribeDataCategoryGroupSample.describeDataCategoryGroupSample();
//Assuming that you have KnowledgeArticleVersion and Questions
//associated with only one category group 'Regions'.
System.assert(describeResult.size() == 2,
'The results should only contain two results: ' + describeResult.size());
for(DescribeDataCategoryGroupResult result : describeResult) {
//Storing the results
String name = result.getName();
String label = result.getLabel();
String description = result.getDescription();
String objectNames = result.getSobject();
//asserting the values to make sure
System.assert(name == 'Regions',
'Incorrect name was returned: ' + name);
System.assert(label == 'Regions of the World',
'Incorrect label was returned: ' + label);
System.assert(description == 'This is the category group for all the regions',
'Incorrect description was returned: ' + description);
System.assert(objectNames.contains('KnowledgeArticleVersion')
|| objectNames.contains('Question'),
'Incorrect sObject was returned: ' + objectNames);
}
}
}
此示例测试该方法。它确保 返回的类别组、类别和关联的对象是 正确。describeDataCategoryGroupStructures
@isTest
private class DescribeDataCategoryGroupStructuresTest {
public static testMethod void getDescribeDataCategoryGroupStructureResultsTest(){
List<Schema.DescribeDataCategoryGroupStructureResult> describeResult =
DescribeDataCategoryGroupStructures.getDescribeDataCategoryGroupStructureResults();
System.assert(describeResult.size() == 2,
'The results should only contain 2 results: ' + describeResult.size());
//Creating category info
CategoryInfo world = new CategoryInfo('World', 'World');
CategoryInfo asia = new CategoryInfo('Asia', 'Asia');
CategoryInfo northAmerica = new CategoryInfo('NorthAmerica',
'North America');
CategoryInfo southAmerica = new CategoryInfo('SouthAmerica',
'South America');
CategoryInfo europe = new CategoryInfo('Europe', 'Europe');
List<CategoryInfo> info = new CategoryInfo[] {
asia, northAmerica, southAmerica, europe
};
for (Schema.DescribeDataCategoryGroupStructureResult result : describeResult) {
String name = result.getName();
String label = result.getLabel();
String description = result.getDescription();
String objectNames = result.getSobject();
//asserting the values to make sure
System.assert(name == 'Regions',
'Incorrect name was returned: ' + name);
System.assert(label == 'Regions of the World',
'Incorrect label was returned: ' + label);
System.assert(description == 'This is the category group for all the regions',
'Incorrect description was returned: ' + description);
System.assert(objectNames.contains('KnowledgeArticleVersion')
|| objectNames.contains('Question'),
'Incorrect sObject was returned: ' + objectNames);
DataCategory [] topLevelCategories = result.getTopCategories();
System.assert(topLevelCategories.size() == 1,
'Incorrect number of top level categories returned: ' + topLevelCategories.size());
System.assert(topLevelCategories[0].getLabel() == world.getLabel() &&
topLevelCategories[0].getName() == world.getName());
//checking if the correct children are returned
DataCategory [] children = topLevelCategories[0].getChildCategories();
System.assert(children.size() == 4,
'Incorrect number of children returned: ' + children.size());
for(Integer i=0; i < children.size(); i++){
System.assert(children[i].getLabel() == info[i].getLabel() &&
children[i].getName() == info[i].getName());
}
}
}
private class CategoryInfo {
private final String name;
private final String label;
private CategoryInfo(String n, String l){
this.name = n;
this.label = l;
}
public String getName(){
return this.name;
}
public String getLabel(){
return this.label;
}
}
}
动态 SOQL
动态 SOQL 是指在运行时使用 Apex 创建 SOQL 字符串 法典。动态 SOQL 使您能够创建更灵活的应用程序。例如,您 可以根据最终用户的输入创建搜索,或使用不同的字段更新记录 名字。
若要在运行时创建动态 SOQL 查询,请通过以下方法之一使用 or 方法。Database.queryDatabase.queryWithBinds
- 当查询返回单个 sObject 时,返回单个 sObject 记录:
sObject s = Database.query(string);
- 当查询返回多个 sObject 时,返回 sObject 的列表 记录:
List<sObject> sobjList = Database.query(string);
- 使用 bind 的映射返回 sObject 列表 变量:
List<sObject> sobjList = Database.queryWithBinds(string, bindVariablesMap, accessLevel);
可以使用 和 方法 任何可以使用内联 SOQL 查询的地方,例如在常规赋值语句和循环中。结果在 与静态 SOQL 查询的处理方式大致相同。Database.queryDatabase.queryWithBindsfor
使用 API 版本 55.0 及更高版本,作为数据库操作用户模式的一部分 功能,请使用该参数在 用户或系统模式。该参数指定 方法在系统模式下运行 () 或用户模式 ()。在系统模式下, 忽略当前用户的对象级和字段级权限,记录 共享规则由类共享关键字控制。在用户模式下, 当前用户的对象权限、字段级安全性和共享规则是 执行。系统模式是默认模式。accessLevelaccessLevelAccessLevel.SYSTEM_MODEAccessLevel.USER_MODE
动态 SOQL 结果可以指定为具体的 sObject,例如 Account 或 MyCustomObject__c,或作为泛型 sObject 数据类型。在运行时,系统 验证查询的类型是否与变量的声明类型匹配。如果 query 未返回正确的 sObject 类型,则会引发运行时错误。因此 您不必从泛型 sObject 转换为具体的 sObject。
动态 SOQL 查询具有与静态查询相同的调控器限制。查看更多 有关调控器限制的信息,请参阅执行 调速器和限制。
有关 SOQL 查询语法的完整说明,请参阅 Salesforce 对象查询语言 (索克尔)在 SOQL 和 SOSL 参考中。
动态 SOQL 注意事项
使用 时,可以在动态 SOQL 查询字符串中使用简单的绑定变量。以下是 允许:
Database.query
String myTestString = 'TestName';
List<sObject> sobjList = Database.query('SELECT Id FROM MyCustomObject__c WHERE Name = :myTestString');
然而 与内联 SOQL 不同,不能在查询字符串中使用绑定变量字段。以下示例 不受支持,并导致错误。
Database.queryVariable does not exist
MyCustomObject__c myVariable = new MyCustomObject__c(field1__c ='TestField');
List<sObject> sobjList = Database.query('SELECT Id FROM MyCustomObject__c WHERE field1__c = :myVariable.field1__c');
你 可以改为将变量字段解析为字符串,并在 动态 SOQL 查询:
String resolvedField1 = myVariable.field1__c;
List<sObject> sobjList = Database.query('SELECT Id FROM MyCustomObject__c WHERE field1__c = :resolvedField1');
(API 版本 57.0 及更高版本)另一种选择是使用该方法。使用此方法,将变量绑定到 查询是直接使用键从 Map 参数解析的,而不是从 Apex 代码变量。这样就无需在 查询。此示例显示了一个 SOQL 查询,该查询将绑定变量用于 帐户名称;其值随 Map 一起传入。Database.queryWithBindsacctBinds
Map<String, Object> acctBinds = new Map<String, Object>{'acctName' => 'Acme Corporation'};
List<Account> accts =
Database.queryWithBinds('SELECT Id FROM Account WHERE Name = :acctName',
acctBinds,
AccessLevel.USER_MODE);
SOQL注射液
SOQL注射是一种技术,用户通过该技术 通过传递 SOQL 语句添加到代码中。这可能发生在 Apex 代码中,只要你的 应用程序依赖于最终用户的输入来构造动态 SOQL 语句,而 没有正确处理输入。
为防止SOQL注射,请使用该方法。此方法将 转义字符 (\) 到传入的字符串中的所有单引号 来自用户。该方法确保将所有单引号视为 将字符串括起来,而不是数据库命令。escapeSingleQuotes
其他动态 SOQL 方法
本主题中的动态 SOQL 示例演示如何使用 和 方法。这些方法还使用动态 SOQL:Database.queryDatabase.queryWithBinds
- Database.countQuery和 :返回数字 动态 SOQL 查询在执行时将返回的记录。Database.countQueryWithBinds
- Database.getQueryLocator和 :创建批处理中使用的对象 Apex 或 视觉力。Database.getQueryLocatorWithBindsQueryLocator
动态 SOSL
动态 SOSL 是指在运行时使用 Apex 创建 SOSL 字符串 法典。动态 SOSL 使您能够创建更灵活的应用程序。例如,您 可以根据最终用户的输入创建搜索,或使用不同的 字段名称。
若要在运行时创建动态 SOSL 查询,请使用 search 方法。例如:query
List<List <sObject>> myQuery = search.query(SOSL_search_string);
下面的示例练习一个简单的 SOSL 查询字符串。
String searchquery='FIND\'Edge*\'IN ALL FIELDS RETURNING Account(id,name),Contact, Lead';
List<List<SObject>>searchList=search.query(searchquery);
动态 SOSL 语句的计算结果为 sObject 列表,其中每个列表包含 特定 sObject 类型的搜索结果。始终返回结果列表 其顺序与在动态 SOSL 查询中指定的顺序相同。从示例 上面,客户的结果首先是“联系人”,然后是“潜在客户”。
搜索方法可以在以下任何位置使用。 可以使用内联 SOSL 查询,例如在常规赋值语句和循环中。结果在很多 与静态 SOSL 查询的处理方式相同。queryfor
动态 SOSL 查询与静态查询具有相同的调控器限制。查看更多 有关调控器限制的信息,请参阅执行 调速器和限制。
有关 SOSL 查询语法的完整说明,请参阅 Salesforce 对象搜索语言 (SOSL)在 SOQL 和 SOSL 参考中。
使用动态 SOSL 返回代码段
若要为搜索结果中的记录提供更多上下文,请使用 SOSL 子句。片段使 确定您要查找的内容。有关代码段如何的信息 生成,请参阅 SOQL 和 SOSL 参考中的 WITH SNIPPET。WITH SNIPPET要在 动态 SOSL 查询,请使用该方法。
WITH SNIPPETSearch.find
Search.SearchResults searchResults = Search.find(SOSL_search_string);
此示例执行包含子句的简单 SOSL 查询字符串。该示例调用打印返回的标题和 片段。您的代码将在网页中显示标题和代码段。WITH SNIPPETSystem.debug()
Search.SearchResults searchResults = Search.find('FIND \'test\' IN ALL FIELDS RETURNING
KnowledgeArticleVersion(id, title WHERE PublishStatus = \'Online\' AND Language = \'en_US\') WITH SNIPPET (target_length=120)');
List<Search.SearchResult> articlelist = searchResults.get('KnowledgeArticleVersion');
for (Search.SearchResult searchResult : articleList) {
KnowledgeArticleVersion article = (KnowledgeArticleVersion) searchResult.getSObject();
System.debug(article.Title);
System.debug(searchResult.getSnippet());
}
SOSL注入
SOSL注入是一种技术,用户通过该技术 使应用程序执行您不希望通过传递的数据库方法 SOSL 语句添加到代码中。每当 应用程序依赖于最终用户输入来构造动态 SOSL 语句和 您没有正确处理输入。
为防止SOSL注入,请使用该方法。此方法 将转义字符 (\) 添加到字符串中的所有单引号中,即 从用户传入。该方法确保所有单引号都 被视为封闭字符串,而不是数据库命令。escapeSingleQuotes
动态 DML
除了查询、描述信息和构建 SOQL 在运行时查询,还可以动态创建 sObjects,以及 使用 DML 将它们插入到数据库中。
若要创建给定类型的新 sObject,请在 sObject 令牌上使用该方法。 请注意,令牌必须转换为具体的 sObject 类型(例如 作为帐户)。例如:newSObject
// Get a new account
Account a = new Account();
// Get the token for the account
Schema.sObjectType tokenA = a.getSObjectType();
// The following produces an error because the token is a generic sObject, not an Account
// Account b = tokenA.newSObject();
// The following works because the token is cast back into an Account
Account b = (Account)tokenA.newSObject();
虽然 sObject 令牌是 Account 的令牌, 它被视为 sObject,因为它是单独访问的。它必须被重新转换回 具体 sObject 类型 Account 来使用该方法。 有关强制转换的更多信息,请参见类和强制转换。tokenAnewSObject
您还可以指定一个 ID,以创建引用现有 记录,以便以后更新。例如:newSObject
SObject s = Database.query('SELECT Id FROM account LIMIT 1')[0].getSObjectType().
newSObject([SELECT Id FROM Account LIMIT 1][0].Id);
请参见SObjectType 类。
动态 sObject 创建示例
此示例演示如何通过 方法,然后使用令牌上的方法创建新的 sObject。Schema.getGlobalDescribenewSObject此示例还包含一个测试方法,该方法 验证帐户的动态创建。
public class DynamicSObjectCreation {
public static sObject createObject(String typeName) {
Schema.SObjectType targetType = Schema.getGlobalDescribe().get(typeName);
if (targetType == null) {
// throw an exception
}
// Instantiate an sObject with the type passed in as an argument
// at run time.
return targetType.newSObject();
}
}
@isTest
private class DynamicSObjectCreationTest {
static testmethod void testObjectCreation() {
String typeName = 'Account';
String acctName = 'Acme';
// Create a new sObject by passing the sObject type as an argument.
Account a = (Account)DynamicSObjectCreation.createObject(typeName);
System.assertEquals(typeName, String.valueOf(a.getSobjectType()));
// Set the account name and insert the account.
a.Name = acctName;
insert a;
// Verify the new sObject got inserted.
Account[] b = [SELECT Name from Account WHERE Name = :acctName];
system.assert(b.size() > 0);
}
}
设置和检索 字段值
在对象上使用 and 方法,以使用 API 名称 表示为 String 的字段,或字段的标记。在以下示例中,API 名称 字段用于:getputAccountNumber
SObject s = [SELECT AccountNumber FROM Account LIMIT 1];
Object o = s.get('AccountNumber');
s.put('AccountNumber', 'abc');
以下示例改用字段的令牌:AccountNumber
Schema.DescribeFieldResult dfr = Schema.sObjectType.Account.fields.AccountNumber;
Sobject s = Database.query('SELECT AccountNumber FROM Account LIMIT 1');
s.put(dfr.getsObjectField(), '12345');
Object 标量数据类型可用作泛型 数据类型,用于设置或检索 sObject 上的字段值。这是等效的 更改为 anyType 字段类型。 请注意,Object 数据类型与 sObject 数据不同 type,可用作任何 sObject 的泛型类型。
注意
使用 API 版本 15.0 保存(编译)的 Apex 类和触发器 如果为字段分配的 String 值太长,则更高会产生运行时错误。
设置和检索 外键
Apex 支持按名称(或外部 ID)填充外键,其方式与 API 相同。要设置 或检索外键的标量 ID 值,使用 or 方法。getput
若要设置或检索与外键关联的记录,请使用 和 方法。请注意,这些方法必须与 sObject 数据一起使用 类型,而不是 Object。例如:getSObjectputSObject
SObject c =
Database.query('SELECT Id, FirstName, AccountId, Account.Name FROM Contact LIMIT 1');
SObject a = c.getSObject('Account');
使用子项时,无需为父级 sObject 值指定外部 ID s对象。如果在父 sObject 中提供 ID,则 DML 操作将忽略该 ID。顶点 假设外键是通过关系 SOQL 查询填充的,该查询始终返回 具有填充 ID 的父对象。如果您有 ID,请将其与子对象一起使用。
例如,假设自定义对象 C1 具有链接到父自定义对象 C2 的外键。您想要创建一个 C1 对象,并将其与名为“AW Computing”的 C2 记录相关联(分配给 值)。您不需要 “AW Computing”记录,因为它是通过父级与 孩子。例如:C2__cC2__r
insert new C1__c(Name = 'x', C2__r = new C2__c(Name = 'AW Computing'));
如果已为 的 ID 赋值,则 将被忽略。如果确实有 ID,请将其分配给对象 (),而不是记录。C2__rC2__c您还可以使用动态 Apex 访问外键。以下示例演示如何获取 使用动态的父子关系中子查询的值 顶点:
String queryString = 'SELECT Id, Name, ' +
'(SELECT FirstName, LastName FROM Contacts LIMIT 1) FROM Account';
SObject[] queryParentObject = Database.query(queryString);
for (SObject parentRecord : queryParentObject){
Object ParentFieldValue = parentRecord.get('Name');
// Prevent a null relationship from being accessed
SObject[] childRecordsFromParent = parentRecord.getSObjects('Contacts');
if (childRecordsFromParent != null) {
for (SObject childRecord : childRecordsFromParent){
Object ChildFieldValue1 = childRecord.get('FirstName');
Object ChildFieldValue2 = childRecord.get('LastName');
System.debug('Account Name: ' + ParentFieldValue +
'. Contact Name: '+ ChildFieldValue1 + ' ' + ChildFieldValue2);
}
}
}