AML 查询与数据操作
AML(Aras Markup Language)是 Aras Innovator 使用的 XML 方言。它同时描述数据、关系结构,以及要在每个 Item 上执行的 action。IOM、REST API 与客户端界面最终都会把操作交给服务器处理;学习 AML 的价值,是看懂请求本身,而不是到处拼接 XML 字符串。
适用范围
- 本文以 Aras Innovator 35 Programmer's Guide 为主要核验基线。
- 基本的
Item / Relationships / property结构在 11 SP9 培训材料中已经存在,跨版本较稳定。 action可以指向内置方法,也可以指向自定义 Method。本文只讲常用内置 action。
第一个查询
下面的 AML 查询编号为 P-10023 的 Part,只返回 id、item_number 和 name:
<AML>
<Item type="Part"
action="get"
select="id,item_number,name">
<item_number>P-10023</item_number>
</Item>
</AML>一条请求可以不写外层 <AML>;当请求里包含多个顶层 Item 时,再用 <AML> 包起来更清楚。
这段请求由三类信息组成:
| 位置 | 示例 | 含义 |
|---|---|---|
Item 属性 | type="Part" | 目标 ItemType |
Item 属性 | action="get" | 要执行的服务器方法 |
| Property 标签 | <item_number>...</item_number> | 查询条件或写入值 |
AML 和 XML 一样区分大小写:Item、Relationships 首字母大写;Property 名称通常是数据库中的小写名称。
用什么工具练习
在开发或测试数据库中使用该版本自带、或官方明确支持的 AML 调试工具。旧培训资料经常使用 AML Studio 和 NASH;它们的入口、认证方式和可用性会随版本及部署策略变化。
不要在生产库试写操作
先用 get 验证类型、属性和选择范围。add、edit、update、delete、purge 等操作会改变数据,并会触发权限、版本和服务器事件。不要为了调试而设置 serverEvents="0",除非你已经证明跳过事件是正确且获批的迁移方案。
Item 的核心属性
最常用的属性如下。它们不是完整 API 清单,版本专有属性应以目标 Release 的 Programmer's Guide 为准。
| 属性 | 用途 | 建议 |
|---|---|---|
type | ItemType 名称 | 人工编写时最易读 |
typeId | ItemType ID | 类型名称未知或由上下文给出时使用 |
action | 要执行的方法 | 明确写出,不猜默认值 |
id | 单个 Item 的 ID | 编辑或删除单条数据时优先使用 |
select | 返回的属性 | 只选业务真正需要的列 |
orderBy | 排序 | 使用目标 ItemType 的属性名 |
page / pagesize | 分页 | 大结果集必须分页 |
maxRecords | 限制搜索数量 | 它限制搜索范围,不等同于稳定分页 |
where | SQL 风格筛选 | 高级用法;优先 Property 条件 |
serverEvents | 是否执行服务器事件 | 默认保留事件 |
select 也能展开 Item Property
对于 Item 类型属性,可以在 select 中声明要展开的属性:
<Item type="Part"
action="get"
select="id,item_number,created_by_id(id,login_name)" />这种写法比返回所有 User 属性更节省传输量。服务端仍会执行当前用户的访问控制。
查询条件
同级 Property 条件默认使用 AND。例如:
<Item type="Part" action="get" select="id,item_number,name">
<state>Released</state>
<name condition="like">Bearing%</name>
</Item>常用 condition
| 条件 | 含义 | 示例值 |
|---|---|---|
eq | 等于;默认条件 | Released |
ne | 不等于 | Obsolete |
gt / ge | 大于 / 大于等于 | 100 |
lt / le | 小于 / 小于等于 | 100 |
like / not like | 通配匹配 / 排除 | P-% |
between / not between | 区间 / 区间外 | 10 and 90 |
in / not in | 集合内 / 集合外 | 'A','B' |
is null / is not null | 空 / 非空 | 标签不写值 |
<Item type="Part" action="get" select="id,item_number,cost">
<cost condition="between">10 and 90</cost>
<description condition="is not null" />
</Item>AND、OR 与 NOT
<Item type="Part" action="get" select="id,item_number,make_buy,state">
<or>
<make_buy>Make</make_buy>
<and>
<make_buy>Buy</make_buy>
<not>
<state>Obsolete</state>
</not>
</and>
</or>
</Item>日期与时间
向 AML 或自定义 SQL 传日期时使用 locale-neutral 形式,例如 2026-03-13T14:30:00。不要把 DateTime.ToString() 的本地化输出直接拼进请求;Windows、Linux 和不同区域设置的输出可能不同。
<Item type="Part" action="get" select="id,item_number,modified_on">
<modified_on condition="ge">2026-03-01T00:00:00</modified_on>
</Item>查询关系
Relationship 本身也是 Item。它通常保存 source_id、related_id,还可以保存数量、顺序等关系属性。
从源 Item 展开关系
<Item type="Part" action="get" select="id,item_number">
<item_number>P-10023</item_number>
<Relationships>
<Item type="Part BOM" action="get" select="id,quantity,related_id">
<related_id>
<Item type="Part" action="get" select="id,item_number,name" />
</related_id>
</Item>
</Relationships>
</Item>返回结构仍保持 Part → Relationships → Part BOM → related_id → Part,因此既能读取关系数量,也能读取子件属性。
反查 Where Used
从目标子件反查引用它的 BOM 行:
<Item type="Part BOM" action="get" select="id,source_id,related_id,quantity">
<related_id>
<Item type="Part" action="get">
<item_number>P-10023</item_number>
</Item>
</related_id>
</Item>如果还要展示父件属性,在 source_id 的 select 中明确展开所需列;不要为了方便使用无界 levels。
递归配置
GetItemRepeatConfig 与关系上的 repeatProp / repeatTimes 可用于递归结构,例如 BOM。用户资料中的递归示例有参考价值,但 repeatTimes="0" 可能返回非常大的结构,且 {this.getID()} 只是代码模板占位符,不是通用 AML 变量。
查看一个有深度上限的 BOM 示例
<Item type="Part"
action="GetItemRepeatConfig"
id="PART_ID"
select="id,item_number,name">
<Relationships>
<Item type="Part BOM"
select="id,quantity,sort_order,related_id"
repeatProp="related_id"
repeatTimes="5">
<related_id>
<Item type="Part" select="id,item_number,name" />
</related_id>
</Item>
</Relationships>
</Item>递归不是免费的
先定义业务允许的最大深度、最大节点数和循环处理策略。对于面向用户的树形浏览,还应考虑 Query Builder 与 Tree Grid View,而不是每次把整棵树拉到浏览器。
常用写操作
add
<Item type="Part" action="add">
<item_number>P-2026-999</item_number>
<name>示例零件</name>
</Item>创建成功后,响应会包含新 Item 的 id 和服务器返回的属性。编号、默认值和权限仍由目标数据库配置决定。
edit
<Item type="Part" action="edit" id="PART_ID">
<name>更新后的名称</name>
</Item>edit 用于编辑一个已存在的 Item。版本化、锁定和事件行为取决于 ItemType 配置与当前状态;不要把它理解成裸 SQL UPDATE。
update
官方 Programmer's Guide 将 update 描述为更新已经锁定的 Item。对于版本化 Item,首次更新仍可能产生新版本;只有在业务和目标 Release 都允许时,才使用 version="0" 抑制升版。它不是“省略 id 即可安全批量更新”的同义词;本文不提供无 id 的批量写法。需要批量变更时,先把选择范围、锁定、版本、权限和事件写成可审计方案,再在隔离环境验证。
<Item type="Part" action="update" id="LOCKED_PART_ID" version="0">
<name>不升版更新示例</name>
</Item>上例只有在调用者已经锁定目标 Item,并且“不升版”确实符合业务规则时才成立。普通业务修改优先使用受支持的编辑流程。
merge
merge 具有 upsert 语义:匹配到 Item 时更新,否则创建。匹配键和版本行为必须明确,否则很容易产生重复或更新错误对象。
delete 与 purge
删除语义必须按版本验证
对于版本化 Item,官方 Programmer's Guide 将 delete 描述为删除该 Item 的全部版本,将 purge 描述为删除单个版本;对于非版本化 Item,两者效果相同。它们都不是可随意恢复的“逻辑删除”按钮。执行前必须在目标 Release 验证权限、依赖关系和备份/恢复方案。
lock 与 unlock
锁用于协作编辑,不是权限替代品。正常应用应通过 IOM 和界面提供的锁定流程操作;数据库层直接清空 locked_by_id 会绕过业务语义与审计。
用 IOM 构建同样的请求
当值来自参数或用户输入时,优先让 IOM 创建 XML 节点,避免字符串拼接造成 XML 破坏或注入。
Innovator inn = this.getInnovator();
Item query = inn.newItem("Part", "get");
query.setAttribute("select", "id,item_number,name");
query.setProperty("item_number", requestedNumber);
Item result = query.apply();
if (result.isError())
{
// 详细错误应进入受控日志;客户端只接收稳定业务消息。
return inn.newError("零件查询失败,请联系管理员并提供操作时间。");
}
return result;isError() 不等于“零条结果”
get 没找到记录时的返回语义可能与异常错误不同,具体判断要结合查询是否预期唯一。常用做法是先检查 isError(),再检查 getItemCount(),不要直接对第 0 项取值。
性能与安全检查表
- 使用
select,不要默认取回所有属性。 - 对可增长的结果使用
page/pagesize,并设计稳定排序。 - 优先 Property 条件;只有无法表达时才用
where。 - 不把值、属性名或表名直接拼进 AML/SQL。
- 不用
serverEvents="0"绕开校验。 - 不用直接 SQL 修改 Innovator 业务表。
- 递归查询设置深度和结果上限。
- 使用普通业务身份验证请求,不能只用管理员证明“能运行”。
版本差异
| 范围 | 应注意的内容 |
|---|---|
| 11 SP9 培训材料 | AML 核心结构仍有参考价值;工具入口、客户端实现和认证已是历史快照 |
| 14+ | 认证和运行平台发生明显变化,但 AML 基本模型仍延续 |
| 35+ | IOM SDK 的交付方式、REST/OAuth 和运行时继续演进;查阅该 Release 文档 |
| 39 | 自定义 SQL 日期/数字格式应再次检查 locale-neutral 表达 |
