Salesforce Connect 与 Apex Connector Framework
Salesforce Connect 使 Apex 代码通过适配器访问外部对象数据。使用 Apex Connector Framework 开发自定义适配器——从外部系统检索数据并在本地合成。Salesforce 以外部对象表示数据——用户和平台可无缝与外部存储的数据交互。DataSource.Connection 类需要 Salesforce Connect 附加许可证。
外部对象与 Apex Connector Framework
外部对象的 Apex 限制:不支持 Apex 托管共享、不支持 Apex 触发器(但 OData 4.0 外部 Change Data Capture 事件支持触发器)。Database.insertAsync() 不能在门户用户上下文中执行——使用 Database.insertImmediate()。Iterable Batch Apex 运行时外部记录存储在 Salesforce 中——作业完成后删除(无论成功与否);QueryLocator Batch 不存储外部数据。若使用 QueryLocator + OData 适配器:启用 Request Row Counts、推荐启用 Server Driven Pagination(服务器端分页防止记录重复处理或跳过)。
可写外部对象:默认只读但可设置为可写——用户和 API 可创建/更新/删除外部数据。DML 操作通过专门的异步方法执行:Database.insertAsync/updateAsync/upsertAsync/deleteAsync——返回 SaveResult 含作业标识符。使用 Database.getAsyncLocator() 获取异步定位器。BackgroundOperation 对象监控写入作业进度。
External Change Data Capture 打包与测试:可将外部 CDC 组件打包到托管包中(选择测试类 → 触发器/测试/外部数据源/外部对象被带入包)。证书不可打包——订阅者组织需有同名有效证书。测试中创建 ChangeEvent 对象,通过 EventBus.publish(event) + Test.getEventBus().deliver() 测试触发器。
Mock SOQL 测试(外部对象):使用与 Data 360 DMO 相同的 SOQL Stub 框架——扩展 System.SoqlStubProvider 重写 handleSoqlQuery(),通过 Test.createSoqlStub() 注册。支持静态/动态 SOQL 查询,包括基本查询和连接查询。
Apex Connector Framework——DataSource.Connection 类:五大核心方法:
- sync():管理员点击 Validate and Sync 时调用——返回外部系统结构元数据。定义 DataSource.Column(text/url/datetime/boolean/picklist/indirectLookup/externalLookup)和 DataSource.Table。ExternalId 列必须声明——值来自该列
- query(QueryContext):SOQL 查询外部对象时调用。QueryContext 始终针对单个表。使用
DataSource.QueryUtils.process()处理过滤/排序/limit/offset——但 QueryUtils 仅用于测试(生产环境需外部系统完成过滤和排序)。处理 COUNT() 聚合查询时检查 aggregation==QueryAggregation.COUNT - search(SearchContext):SOSL 查询或全局搜索时调用——SearchContext 可含多个 TableSelection(支持多表联合搜索)。为每个 tableSelection 返回 DataSource.TableResult
- upsertRows(UpsertContext):外部对象创建/更新时调用——根据 ExternalId 决定 POST(插入)或 PUT/PATCH(更新)。每行一个 callout 可能触及 callout 限制
- deleteRows(DeleteContext):外部对象删除时调用——对每个 externalId 执行 DELETE callout
DataSource.Provider 类:
- getAuthenticationCapabilities():声明认证类型——ANONYMOUS/OAUTH 等。始终返回静态列表(不查询数据库/不 callout/不动态逻辑——变化会导致难以排查的错误)
- getCapabilities():声明功能——ROW_QUERY(SOQL)/SEARCH(SOSL+全局搜索)/ROW_CREATE/ROW_UPDATE/ROW_DELETE/PICKLIST/MULTI_PICKLIST。同样始终返回静态列表
- getConnection():返回 DataSource.Connection 实例。修改并保存 Connection 类后需重新保存 Provider 类——否则自定义适配器不在 Type 字段选项中显示
关键概念:
- External IDs:每个外部对象有 External ID 标准字段——唯一标识记录。自定义适配器必须声明 ExternalId 列并提供值。不要使用敏感数据作为 External ID(Salesforce 有时存储这些值)
- 认证:ConnectionParams 根据 Identity Type 提供凭据——Named Principal=外部数据源定义、Per User(查询/搜索)=用户认证设置、Per User(管理连接)=外部数据源定义
- OAuth 令牌刷新:抛出
DataSource.OAuthTokenExpiredException时自动刷新——使用刷新令牌重建 Connection。需要认证提供程序提供刷新令牌(添加 refresh_token scope 或 access_type=offline) - Callout:认证参数封装在 ConnectionParams 中——OAuth(
this.connectionInfo.oauthToken)/Basic Auth(username/password)。若所有 callout 使用命名凭据——外部数据源可设置 No Authentication - 分页:Server-Driven(外部系统控制——声明 QUERY_PAGINATION_SERVER_DRIVEN 能力——提供 queryMoreToken)+ Client-Driven(使用 LIMIT/OFFSET——声明 QUERY_TOTAL_SIZE 则返回 totalSize)
- queryMore:默认不支持——需实现分批遍历大量结果集(默认 500 条/批)。Server-Driven:TableResult 返回 queryMoreToken 直到最终批不返回 token。Client-Driven:QUERY_TOTAL_SIZE 声明 + TableResult 设置 totalSize;或每次请求多一条记录判断是否有更多数据
- 过滤器:QueryContext 含一个 TableSelection——SearchContext 可含多个。Filter 类含 columnName/columnValue/type(EQUALS/LESS_THAN/AND_/OR_/NOT_)。子过滤器(compound filter)在 subfilters 属性中。最佳实践:尽量在外部系统中完成所有过滤——减少返回数据量避免触及 Governor Limit
框架限制:DataSource.Connection/Provider 中不允许 DML 操作。所有 Apex Governor Limit 适用。测试方法不支持 Web Service Callout——测试外部对象使用动态 SOQL(静态 SOQL 测试外部对象失败)。Double 精度超过 18 位有效数字会丢失——使用 Decimal。String 长度超过 255 映射到长文本区域字段。
完整示例:GitHub Issues 适配器(间接查找关系/外部查找关系/Date+Picklist 字段/DML 操作)、GitHub 适配器(间接查找关系 Contact.github_username)、Google Drive 适配器(OAuth callout/测试中 mock 响应替代真实 callout)、Google Books 适配器(API 最大 40 条限制/禁用排序列)。所有适配器实现 DataSource.Connection(sync+query+search+/−upsertRows+/−deleteRows)和 DataSource.Provider(认证+功能+连接)。
Apex Connector Framework 是将任何外部数据源无缝集成到 Salesforce 中的终极工具——从 REST API 到数据库再到云存储——通过自定义适配器实现深度连接。