如何在不修改表的情况下向实体添加扩展字段
抱歉,我需要您提供要翻译的具体文本内容(除代码块和 URL 之外),才能为您进行简体中文翻译。请把文章的正文粘贴在这里,我会保持原有的格式、Markdown 语法以及技术术语不变,只翻译文字部分。谢谢!
Source: …
视频演示
基于内置于 Nop 平台的 NopOrm 引擎,向任意实体添加 扩展属性 非常方便。
这些属性支持查询和排序,且在使用上与实体的内置属性完全一致。
如何启用扩展字段
- 在 Excel 数据模型的 数据表 中 添加
use‑ext‑field标记。 - 系统将全局启用扩展字段支持,并将值存储在
nop_sys_ext_field表中。
nop_sys_ext_field 表结构
| 列名 | 类型 |
|---|---|
entity_name | VARCHAR |
entity_id | VARCHAR |
field_name | VARCHAR |
field_type | INTEGER |
decimal_value | DECIMAL |
date_value | DATE |
timestamp_value | TIMESTAMP |
string_value | VARCHAR |
实际存储值的列取决于 field_type(例如 decimal_value、date_value 等)。
生成的 ORM 映射
编译期间,“ 标签读取 use‑ext‑field 配置并自动生成以下映射:
<!-- ORM mapping generated by the compiler -->
如果在一对多关系中设置了 keyProp,则将该属性标记为集合的 唯一标识符。IOrmEntitySet 集合随后提供扩展方法 prop_get / prop_set,可以直接通过此键访问项。
在 Java 中访问扩展字段
// 读取
IOrmKeyValueTable field = (IOrmKeyValueTable) entity.getExtFields()
.prop_get("fldA");
// 写入
entity.getExtFields().prop_set("fldA", value);
在 XScript / EQL 中访问扩展字段
extFields.fldA.string // 等价于 entity.getExtFields().prop_get("fldA").getString()
fldA 通过根据 keyProp 在一对多集合中定位唯一记录来解析。
使用别名以简化访问
您可以定义 别名,使扩展字段看起来像普通属性。
<!-- Alias configuration XML -->
添加别名配置后:
extFldA和extFldB成为实体的普通属性。- 在 Java 中,您可以使用
entity.prop_get("extFldA"),或者如果已生成代码,则使用更方便的方法:
entity.getExtFldA(); // getter
entity.setExtFldA(value); // setter
当生成了 getter/setter 后,您 不能 再使用 entity.prop_get 访问该字段,因为 prop_get 仅保留给实体上 不存在 的属性。
若想统一获取内置字段和扩展字段,可以调用:
entity.orm_propValueByName(name);
或使用反射工具,例如 BeanTool.getProperty(entity, propName)。
使用扩展字段的 EQL 查询
扩展字段可以在过滤、排序和投影中像内置字段一样使用:
select o.extFldA
from MyEntity o
where o.extFldA = '123'
order by o.extFldA
GraphQL 暴露
将属性定义添加到 xmeta 文件,以便 GraphQL 能查询扩展字段:
<!-- xmeta configuration for GraphQL -->
本地‑扩展表(性能优化)
所有扩展字段默认存储在 nop_sys_ext_field 中,这可能导致表体积过大并降低性能。
为了解决此问题,可在实体表上添加 local‑ext 标记。系统随后会为该实体生成一个 配套的扩展字段表,通常命名为:
original_table_name + '_ext'
示例: nop_sys_notice_template_ext
本地‑扩展表的结构与 nop_sys_ext_field 相同,唯一的区别是它 不 包含 entityName 列(因为不需要按实体名称过滤)。
注意: 许多低代码平台使用单一的垂直表来存储所有数据,需要硬编码的垂直‑转‑水平转换。Nop 通过可选的本地‑扩展表的做法,避免了这种复杂性。
Example: Unit Test TestExtFields
<!-- Unit test configuration XML -->
任何 to‑many 关系都可以通过 keyProp 属性进行配置,以在集合中唯一标识一条记录。
别名 ext.fldA.string 等价于:
((IOrmEntitySet) entity.getExt()).prop_get("fldA").getString();
摘要
- 扩展字段 通过
use‑ext‑field添加,并存储在nop_sys_ext_field中。 - ORM 生成器会创建一个带有
keyProp的to‑many关系(extFields)。 - 可以通过
prop_get/prop_set、生成的 getter/setter,或统一的orm_propValueByName进行访问。 - 别名 将扩展字段转换为常规属性,可在 Java、XScript、EQL 和 GraphQL 中使用。
- 对于大规模使用,添加
local‑ext为每个实体创建专用表,以提升性能。
扩展字段访问和代码生成
命名快捷方式
在上面的配置中,extFldA 等价于 ext.fldA.string。
这为访问扩展字段所需的复杂路径提供了一个简洁的名称。
代码生成标志
如果标记了 notGenCode,则在代码生成期间不会为该属性生成 Java 的 getter/setter 方法。
必须通过如下方法获取值:
entity.prop_get("extFldB");
统一访问语法
在 XScript 或 XPL 模板语言中,扩展属性和普通属性的访问语法完全相同。可以直接赋值:
entity.extFldB = true;
EQL 自动识别
在 EQL 查询语言中,keyProp 会被自动识别用于结构转换。
EQL 转换
简单属性访问
select o.children.myKey.value
from MyEntity o
转换为
select u.value
from MyEntity o
left join Children u
on o.id = u.parent_id
and u.key = 'myKey';
扁平化集合
只要集合具有唯一标识符,就可以始终将其扁平化为具有唯一访问路径的关联属性。ORM 引擎的 EQL 语言会将类似 o.a.b.c 的嵌套属性转换为相应的表连接查询。
多键过滤
select o.name
from MyEntity o
where o.children.myKey1.intValue = 3
and o.children.myKey2.strValue like 'a%';
转换为
select o.name
from MyEntity o
left join Children u1
on o.sid = u1.parent_id
and u1.key = 'myKey1'
left join Children u2
on o.sid = u2.parent_id
and u2.key = 'myKey2'
where u1.intValue = 3
and u2.strValue like 'a%';
一对多 → 一对一(通过键过滤)
select o.key1,
o.children.myKey1.value,
o.children.myKey2.value
from MyEntity o;
转换为
select o.key1,
u1.value,
u2.value
from MyEntity o
left join Children u1
on o.sid = u1.parent_id
and u1.key = 'myKey1'
left join Children u2
on o.sid = u2.parent_id
and u2.key = 'myKey2';
确定性转换规则
根据已定义的规则,从 o.children.myKey 中提取相关表在数学层面上是一次 确定性局部转换。ORM 引擎会自动应用此规则,将属性路径表达式转换为相应的 SQL 连接。