How to Add Extended Fields to Entities Without Modifying Tables
Source: Dev.to
Video Demo
Based on the NopOrm engine built into the Nop platform, it’s very convenient to add extended properties to any entity.
These properties support querying and sorting, and are completely consistent with the entity’s built‑in properties in terms of usage.
How to enable extended fields
- Add the
use‑ext‑fieldtag to the data table in the Excel data model. - The system will enable global extended‑field support and store the values in the
nop_sys_ext_fieldtable.
nop_sys_ext_field table structure
| Column Name | Type |
|---|---|
entity_name | VARCHAR |
entity_id | VARCHAR |
field_name | VARCHAR |
field_type | INTEGER |
decimal_value | DECIMAL |
date_value | DATE |
timestamp_value | TIMESTAMP |
string_value | VARCHAR |
The column that actually stores the value depends on field_type (e.g., decimal_value, date_value, …).
Generated ORM mapping
During compilation the “ tag reads the use‑ext‑field configuration and automatically generates the following mapping:
<!-- ORM mapping generated by the compiler -->
If keyProp is set in a one‑to‑many relationship, it marks that property as the unique identifier of the collection. The IOrmEntitySet collection then provides extension methods prop_get / prop_set that can directly access items by this key.
Accessing extended fields in Java
// read
IOrmKeyValueTable field = (IOrmKeyValueTable) entity.getExtFields()
.prop_get("fldA");
// write
entity.getExtFields().prop_set("fldA", value);
Accessing extended fields in XScript / EQL
extFields.fldA.string // equivalent to entity.getExtFields().prop_get("fldA").getString()
fldA is resolved by locating the unique record in the one‑to‑many collection according to keyProp.
Using Aliases for Simpler Access
You can define aliases so that extended fields look like ordinary properties.
<!-- Alias configuration XML -->
After adding the alias configuration:
extFldAandextFldBbecome normal properties of the entity.- In Java you can use
entity.prop_get("extFldA")or, if code is generated, the convenient methods:
entity.getExtFldA(); // getter
entity.setExtFldA(value); // setter
When getters/setters are generated, you cannot use entity.prop_get for that field any more, because prop_get is reserved for properties that do not exist on the entity.
For a unified way to retrieve both built‑in and extended fields you can call:
entity.orm_propValueByName(name);
or use reflection utilities such as BeanTool.getProperty(entity, propName).
EQL queries with extended fields
Extended fields can be used in filtering, sorting, and projection exactly like built‑in fields:
select o.extFldA
from MyEntity o
where o.extFldA = '123'
order by o.extFldA
GraphQL Exposure
Add the property definition to the xmeta file so that GraphQL can query the extended fields:
<!-- xmeta configuration for GraphQL -->
Local‑Ext Tables (Performance Optimisation)
All extended fields are stored in nop_sys_ext_field by default, which can cause a large table and degrade performance.
To mitigate this, add the local‑ext tag to the entity table. The system will then generate a paired extended‑field table for that entity, typically named:
original_table_name + '_ext'
Example: nop_sys_notice_template_ext
The local‑ext table has the same structure as nop_sys_ext_field except it does not contain the entityName column (filtering by entity name is unnecessary).
Note: Many low‑code platforms use a single vertical table to store all data, requiring a hard‑coded vertical‑to‑horizontal transformation. Nop’s approach with optional local‑ext tables avoids that complexity.
Example: Unit Test TestExtFields
<!-- Unit test configuration XML -->
Any to‑many relationship can be configured with a keyProp attribute to uniquely identify a record inside the collection.
The alias ext.fldA.string is equivalent to:
((IOrmEntitySet) entity.getExt()).prop_get("fldA").getString();
Summary
- Extended fields are added via
use‑ext‑fieldand stored innop_sys_ext_field. - The ORM generator creates a
to‑manyrelation (extFields) with akeyProp. - Access is possible through
prop_get/prop_set, generated getters/setters, or the unifiedorm_propValueByName. - Aliases turn extended fields into regular‑looking properties, usable in Java, XScript, EQL, and GraphQL.
- For large‑scale usage, add
local‑extto create a dedicated table per entity, improving performance.
Extended Field Access and Code Generation
Naming Shortcut
In the configuration above, extFldA is equivalent to ext.fldA.string.
This gives a simple name to the complex path required for accessing extended fields.
Code Generation Flag
If notGenCode is marked, the getter/setter methods in Java will not be generated for this property during code generation.
Values must be obtained via methods such as:
entity.prop_get("extFldB");
Uniform Access Syntax
In XScript or XPL template languages, the access syntax for extended properties and ordinary properties is identical. You can directly assign values:
entity.extFldB = true;
EQL Automatic Recognition
In the EQL query language, keyProp is automatically recognized for structural transformation.
EQL Transformations
Simple Property Access
select o.children.myKey.value
from MyEntity o
Transforms to
select u.value
from MyEntity o
left join Children u
on o.id = u.parent_id
and u.key = 'myKey';
Flattening Collections
As long as a collection has a unique identifier, it can always be flattened into an associated property with a unique access path. The ORM engine’s EQL language handles the transformation of nested properties like o.a.b.c into the appropriate table‑join queries.
Multiple Key Filters
select o.name
from MyEntity o
where o.children.myKey1.intValue = 3
and o.children.myKey2.strValue like 'a%';
Transforms to
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%';
One‑to‑Many → One‑to‑One via Key Filter
select o.key1,
o.children.myKey1.value,
o.children.myKey2.value
from MyEntity o;
Transforms to
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';
Deterministic Transformation Rule
According to the defined rules, extracting the related table from o.children.myKey is, at the mathematical level, a deterministic local transformation. The ORM engine applies this rule automatically, converting property‑path expressions into the corresponding SQL joins.