一、使用元数据关系字段
本单元探索元数据关系字段——如何在自定义元数据类型之间、与实体定义、与字段定义之间建立关系,并应用到 Acme Services 不断扩展的支持产品需求。
学完本单元,你将能够:在需要时使用元数据类型关系字段、声明式定义一个带关系字段的自定义元数据类型、向带关系字段的自定义元数据类型添加记录。
我们的用例——扩展支持配置
Acme Services 正在扩张,简单 Support Tier 模型(来自 Basics 模块)需要扩展。新需求:把支持产品绑定到支持层级——专用电话线 → VIP 层级、在线聊天 → Premium 层级、邮件支持 → Free 层级。挑战在于:不同对象上的 picklist 值可能与自定义元数据类型记录名不匹配,直接映射会失效。解决方案是自定义元数据类型的关系,支持三种类型:与另一个自定义元数据类型的关系(如 Support Level Mapping → Support Tier)、与 Entity Definition 的关系(标准或自定义对象定义)、与 Field Definition 的关系(自定义或标准字段)。关系 = 跨对象与类型的灵活映射。
我们的用例——数据模型设计
架构决策很重要:Support Product 是自定义对象,而非自定义元数据类型。为什么?因为不同 org 有不同的产品——它们是业务数据,不是应用配置;你不希望包升级覆盖订阅者的产品目录。Support Level Mapping 才是自定义元数据类型,因为它定义了产品如何映射到支持层级——这是应随包一起传播的配置逻辑。对象 = 产品数据(org 特定);CMT = 映射配置(可打包、可部署)。这个"什么是数据、什么是元数据"的区分是基本设计模式。
创建自定义对象与字段
设置 Support Product 自定义对象:Setup 创建自定义对象 Support Product(保存后启动 Custom Tab 向导),添加自定义 picklist 字段 Support Level(值 Free、Premium、VIP),然后录入三条产品记录——Dedicated Phone Line → VIP、Online Chat → Premium、Email Support → Free。这些产品记录代表 Acme Services 实际销售的内容。下一步创建 Support Level Mapping CMT,把这些产品桥接到现有的 Support Tier 元数据。
创建带关系字段的自定义元数据类型
创建 Support Level Mapping 自定义元数据类型,含四个字段:Support Tier Object(Metadata Relationship → Entity Definition,指向 Support Product 对象定义)、Support Tier Field(Metadata Relationship → Field Definition,指向 Support Level picklist 字段,Controlling Field 为 Support Tier Object)、Support Tier(Metadata Relationship → Support Tier CMT)、Support Tier Name(Text,存储显示名)。第一个关系字段是关键——它引用的是 Entity Definition(对象本身)而非具体记录,为 Apex 代码开启了强大的动态查找能力。Entity Definition 与 Field Definition 关系类型让 Apex 能在运行时动态解析该用哪个对象与字段。
创建自定义元数据类型记录
填充 Support Level Mapping 记录。记录 Free - Support Product:Support Tier Object → Support Product、Support Tier Field → Support Level、Support Tier → Bronze、Support Tier Name → Free。这条记录把 Support Product 对象 Support Level 字段上的 Free picklist 值映射到 Bronze 支持层级。类似地创建 Premium → Silver、VIP → Gold 的映射。现在 Apex 代码可以动态解析"这个产品的支持层级适用哪个 tier"。这些关系可在 SOQL 与 Apex 中查询,代码可以遍历关系图、用可配置元数据实现复杂业务逻辑——改映射只需编辑元数据记录,无需改代码。
二、用自定义元数据类型开发与测试
本单元转向开发者视角——在 Apex 中访问自定义元数据类型、运行 SOQL 查询、编写处理元数据记录的测试类。
学完本单元,你将能够:用代码访问自定义元数据类型、用 Apex 测试自定义元数据类型。
Apex 与自定义元数据类型
在 Apex 中访问自定义元数据类型与自定义对象类似,只是类型后缀用 __mdt 而非 __c(字段后缀仍是 __c)。SOQL 示例:查询某类型所有记录的值。CRUD 能力:CREATE(支持,subscriber 可控、命名空间内可见)、READ(支持)、UPDATE(支持,内存中编辑、不可 upsert)、DELETE(不支持)。不允许 DML 操作,部署用 Metadata.DeployContainer。审计字段(CreatedDate、CreatedBy 等)不可编辑。核心好处:记录是元数据,所以随包自动传播,无需手动数据加载。
测试自定义元数据类型
与测试普通数据记录不同,自定义元数据类型确实需要测试——它们影响 org 的行为。好消息:你不需要 @IsTest(SeeAllData=true) 注解。自定义元数据类型像工作流与验证规则一样处理:Apex 测试类自动看到所有自定义元数据类型、字段与记录,无需特殊注解,测试看起来就像标准 Apex 测试用例。这意味着你可以写干净、隔离的测试,测试不依赖 org 数据(默认 SeeAllData=false),在任何环境都能可靠运行。测试模式很简单:查询需要的元数据记录,执行依赖它的代码,根据元数据值断言预期行为。
三、保护自定义元数据类型与记录
本单元探索如何保护自定义元数据类型与记录——控制可见性、字段可编辑性,以及在受管/非受管包上下文中的访问。
学完本单元,你将能够:识别保护自定义元数据类型的选项、控制字段的可编辑性、保护自定义元数据记录。
保护自定义元数据类型——可见性级别
三种可见性级别:Public——Apex、公式、Flow、API(有 Customize Application 权限)都可访问,类型/字段/未保护记录在 Setup 可见,任何包类型可用。Protected(命名空间级)——受管包中只有同一命名空间的 Apex 能看类型,类型名与记录仅在公式引用时可见,只能在 developer/scratch org 创建。Package Protected(2GP)——二代受管包中只有同一包的 Apex 能看类型,多加一层:同一命名空间但不同包 → 无访问。选择:要灵活性用 Public,要保护秘密用 Protected。
保护自定义元数据类型——额外考量
几个重要细节。首先,默认情况下 API 读访问是受限的,即使对 Public 类型也如此(这是推荐的安全姿态);管理员通过配置文件与权限集授予访问,而非全局授予。Apex 系统模式例外:Apex 以 system mode 运行、忽略用户权限,不受 Restrict access 设置影响,你的 Apex 代码始终对所有对象/字段有完整访问权。用户模式(Visualforce 组件、邮件模板、Aura)则尊重用户权限与共享。关键:Public → Protected 的转换是单向的,一旦设为 protected 就无法改回 public。所以打包前就要规划好可见性策略。
保护自定义元数据记录
不必保护整个类型,也可以保护public 类型内的单条记录。受管包中受保护记录的访问规则:与记录同一命名空间的代码可读;与类型同一命名空间的代码可读该类型的记录;不同命名空间的代码不能读;订阅者创建的代码与未受管包代码都不能读。关键行为:把类型从 Protected 改成 Public 后,受保护记录保持受保护、其他记录变 public;订阅者不能创建 protected 类型的记录;在 protected 类型上用 Setup 创建记录时 Protected Component 默认勾选。Public 类型 + protected 记录 = 灵活又安全的混合。
字段可管理性
包发布后字段可编辑性的三种选项:Upgradeable(包开发者控制)——开发者可通过包升级编辑,订阅者不能改,用于跨 org 需一致的字段(法律要求、定价公式);Subscriber Editable(订阅者可编辑)——订阅者安装后可编辑,包升级不覆盖订阅者的改动,用于因 org 而异的字段(本地阈值、区域设置);Locked(锁定)——开发者与订阅者都不能改,用于不可变常量。示例 Support Tier 字段:Default Discount → Upgradeable(法律控制折扣率)、Minimum Spending → Subscriber Editable(因 org 而异)。发布前规划好——发布后更改受限。
综合起来——受管包示例
Support Tier 在受管包里:Support Tier(Upgradeable,Developer 可编辑)、Support Level Mapping(Subscriber Editable,public 记录上 Developer + Subscriber 都可编辑)。受管包规则:开发者始终可编辑 upgradeable 字段;订阅者可编辑 public 记录上的 subscriber-editable 字段;protected 记录上的 subscriber-editable 字段发布后锁定。非受管包则订阅者什么都能编辑。教训:字段管理很快会变复杂,打包前务必规划好字段管理策略。
四、打包自定义元数据类型
本单元完成旅程——打包自定义元数据类型与记录、上传与安装包,并理解字段可管理性如何影响包升级。
学完本单元,你将能够:打包自定义元数据类型、打包类型关联的记录、上传包、在订阅者 org 安装包、升级包。
打包自定义元数据类型
把自定义元数据类型加入包:Setup → Package Manager → 选择或创建包(受管包每个 DE org 一个、需命名空间;非受管包无需命名空间),点 Add → 组件类型选 Custom Metadata Type → 选 Support Tier → Add to Package。注意:类型与字段被加入,但记录没有——加记录是单独的步骤。受管包、非受管包、扩展与 change set 都支持自定义元数据类型。
打包自定义元数据记录
记录必须单独添加:点 Add → 组件类型选 Support Tier(你特定的类型,而非通用的 "Custom Metadata Type")→ 选所有记录 → Add to Package。小技巧:添加记录时类型会自动包含,所以如果从记录开始,可以跳过单独加类型的步骤。验证包里两个都在:Custom Metadata Type(Support Tier 定义)与 Support Tier 记录(Bronze、Silver、Gold 数据)。两者会一起部署,无需手动加载数据。
上传包
所有组件加齐后上传包:点 Upload,设版本名(如 Fall 2019),选发布类型(Managed - Released 用于生产,Managed - Beta 用于测试、首次发布推荐 Beta),点 Upload 等确认邮件。上传含自定义元数据类型的包与上传其他包没有区别——类型与记录作为元数据传播,目标 org 无需数据导出或手动加载。
安装包
在订阅者 org 安装:登出开发 org → 用确认邮件里的安装链接 → 登录目标 org → 选择 Install for Admins Only。安装后验证两件事:Setup 的 Custom Metadata Types 里 Support Tier 类型出现;点 Manage Records 确认所有记录(Bronze、Silver、Gold)都在。应用配置记录在几次点击内就从一个 org 迁移到另一个,无需数据迁移脚本——这是自定义元数据类型的根本优势。
升级带自定义元数据类型的包
应用开发很少一次完成,升级必须尊重字段可管理性设置。Upgradeable 字段(如 Default Discount):开发者的改动包含在升级中、订阅者的值被升级覆盖,用于集中控制的配置。Subscriber Editable 字段(如 Minimum Spending):开发者的改动不包含在升级中、订阅者的值被保留,用于 org 特定的配置。测试:在开发 org 同时改两类字段 → 上传新版本 → 重新安装到订阅者 org → 验证 upgradeable 字段更新了、subscriber-editable 字段保留了。
五、转换自定义设置
本单元收尾——学习如何把现有的列表型自定义设置转换为自定义元数据类型,为准备现代化配置架构的 org 提供一条实用的迁移路径。
学完本单元,你将能够:识别把列表型自定义设置转换为自定义元数据类型的方法、重构 org 的 Apex 代码以支持自定义元数据类型。
切换
一个人,三天——可以转换最多 10 个自定义设置对象、重构最多 20 万行 Apex 代码。重要:只转换列表型(list)自定义设置,不要转换层级型(hierarchy)自定义设置——层级模型无法映射到自定义元数据类型。三步流程:第 1 步把自定义设置对象转为自定义元数据类型(手动或通过 XML/XSLT 转换),保持相同字段名以免后面改 Apex;第 2 步把 Apex 引用里的 __c 后缀改成 __mdt(仅类型后缀,字段保持 __c);第 3 步用 SOQL 查询替换 Apex 方法(如 getAll() → SELECT ... FROM ...__mdt)。完成后打包部署。
第 3 步:用 SOQL 查询替换 Apex 代码
元数据部署、引用改名后,重构 Apex 逻辑:用 SOQL 查询替换 Apex 方法(❌ MySettings__c.getAll() → ✅ SELECT CustomField__c FROM MyMetadata__mdt);删除废弃的自定义对象及其支撑代码——包括插入配置记录的 post-install 脚本(不再需要,记录现在是元数据)、创建 mock 配置记录的测试代码(测试现在全局可见真实元数据记录);部署重构后的代码到生产。结果:org 配置变成元数据驱动,记录随包部署,没有数据加载脚本,测试更干净,升级更顺畅。
程序化 CMT 开发——关键要点
程序化自定义元数据类型开发的四大支柱:关系字段——Entity Definition、Field Definition、CMT-to-CMT,实现灵活的元数据驱动数据模型;Apex 访问——用 SOQL 查询(__mdt 后缀),支持 CRU 不支持 Delete,测试无需 SeeAllData(元数据全局可见);保护——类型三档(Public/Protected/Package Protected)、public 类型内可单独保护记录、字段三档(Upgradeable/Subscriber Editable/Locked);打包——类型与记录分开加入包、升级尊重字段可管理性、首次上传前规划可见性与可管理性。元数据驱动开发 = 可配置、可部署、可升级的应用。
文章来源:Trailhead - Programmatic Development with Custom Metadata Types





























