Skip to content

Meta Table ​

ArchForge provides a low-code meta table capability that lets administrators define tables, columns, indexes, and constraints from the admin UI, and then automatically create the physical PostgreSQL table, perform schema evolution, manage row data, and generate full-stack boilerplate code.

Features ​

Visual Table Design ​

Define a table through the admin UI with the following metadata:

PropertyDescription
Table codeUnique machine-readable identifier, used as the physical table name suffix
Table nameHuman-readable display name
Table prefixPhysical table prefix, defaults to meta_. Lowercase letters, digits and _, at most 32 characters; prefix + code may not start with sys_, qrtz_, pg_, sql_, information_schema_ or flyway_
StatusEnabled (1) or disabled (0)
DescriptionOptional notes

Column Types ​

Meta tables support the following column types:

TypePostgreSQL mappingNotes
STRINGVARCHAR(length)Default length 255
TEXTTEXTLong text
INTEGERBIGINTWhole numbers
DECIMALNUMERIC(precision, scale)Defaults to NUMERIC(18,2)
BOOLEANBOOLEANTrue/false
DATEDATECalendar date
DATETIMETIMESTAMPWithout timezone
TIMESTAMPTZTIMESTAMPTZWith timezone
ENUMVARCHAR(length)Values must be items of the column's dictionary (dictCode)
JSONJSONBJSON documents — send an object / array, or JSON text
GEOJSONB{"lat": …, "lng": …} (object or JSON text)
FILEVARCHAR(512)File reference
UUIDUUIDUUID values
ARRAYelement_type[]Arrays of STRING, INTEGER, DECIMAL, or BOOLEAN

Column Properties ​

Each column can be configured with:

  • Length / precision / scale — for string, decimal, and array element sizing. Validated against PostgreSQL's limits: length ≤ 10485760, precision ≤ 1000, 0 ≤ scale ≤ precision (18 when unset); an ARRAY element type must be STRING, INTEGER, DECIMAL or BOOLEAN
  • Nullable / required — controls NOT NULL
  • Default value — checked against the column type before any DDL runs: numbers are parsed and re-printed, text is quoted; a value that does not fit the type is rejected
  • Unique — enforces unique values among non-deleted rows; a duplicate is a validation error (10408, <column> 的值已存在), not a server error
  • Index / index type / index group — single or composite indexes (BTREE, GIN, GIST, FULLTEXT)
  • Searchable — exposed in the data list search form
  • List visible — shown in the data grid by default
  • Tenant / owner column — flags for multi-tenant or row-owner semantics
  • Dictionary — ENUM values are checked against the column's dictionary; a missing or empty dictionary rejects every write (fail-closed). Legacy inline options still count when the dictionary has not been created
  • Reference table / column / display expression — for REFERENCE columns. The display expression may only use ref.<column> (optionally ::text), 'string literals' and ||, e.g. ref.username || ' (' || ref.email || ')'; functions, operators and sub-queries are rejected. The referenced table must be the table itself or a registered meta table — platform tables (sys_* and the like) can never be joined, so a display expression cannot read e.g. sys_user.password

Schema Evolution ​

When a table definition is updated, the SchemaDiffEngine compares the old and new column lists and produces an ordered migration plan:

  1. RENAME_COLUMN
  2. DROP_COLUMN
  3. ADD_COLUMN
  4. ALTER_TYPE
  5. ALTER_DEFAULT
  6. ALTER_NULL
  7. ALTER_INDEX

The AlterTableDdlGenerator converts each change into safe PostgreSQL ALTER TABLE DDL. The migration history is persisted in sys_meta_table_migration and can be exported as a Flyway SQL file.

Data CRUD ​

Once a table is defined and the physical table exists, you can:

  • List paginated rows with dynamic filters
  • Insert new rows
  • Update existing rows
  • Soft-delete rows
  • Import data from CSV or JSON

Every write is validated against the column definitions first; invalid values, ENUM values outside the dictionary and duplicates on unique columns all come back as 10408 with the column name.

  • Export data to EXCEL, CSV, or JSON

Code Generation ​

Each meta table can generate a full module scaffold using FreeMarker templates:

Backend (example/<tableCode> by default)

  • build.gradle.kts
  • domain/{Entity}.java
  • dao/{Entity}Dao.java
  • dto/{Entity}CreateRequest.java
  • dto/{Entity}UpdateRequest.java
  • dto/{Entity}ListRequest.java
  • dto/{Entity}Response.java
  • dto/{Entity}PageResult.java
  • service/{Entity}Service.java
  • rest/{Entity}Controller.java
  • Error code / exception / project module classes
  • Integration test skeleton

Frontend (src/views/<tableCode> by default)

  • src/api/<tableCode>.ts
  • src/router/modules/<tableCode>.ts
  • src/views/<tableCode>/utils/types.ts
  • src/views/<tableCode>/utils/hook.tsx
  • src/views/<tableCode>/index.vue
  • src/views/<tableCode>/form/index.vue

The generated controller delegates list/export/import to MetaTableCrudService and reuses the dynamic table runtime.

Data Model ​

sys_meta_table ​

FieldTypeDescription
idBIGINTPrimary key
table_codeVARCHAR(64)Unique table code
table_nameVARCHAR(128)Display name
descriptionVARCHAR(512)Notes
table_prefixVARCHAR(32)Physical prefix, default meta_
statusINTEnabled 1 / disabled 0
creator_idBIGINTCreated by
create_timeTIMESTAMPCreation time
updater_idBIGINTUpdated by
update_timeTIMESTAMPUpdate time
deletedINTSoft-delete flag

sys_meta_table_column ​

FieldTypeDescription
idBIGINTPrimary key
table_idBIGINTForeign key to sys_meta_table
column_codeVARCHAR(64)Column code
column_nameVARCHAR(128)Display name
data_typeVARCHAR(32)One of the supported column types
lengthINTLength or max size
precisionINTNumeric precision
scaleINTNumeric scale
nullableINT1 = nullable, 0 = not null
default_valueVARCHAR(255)Default value
is_uniqueINTUnique flag
is_requiredINTRequired flag
is_searchableINTSearchable flag
is_list_visibleINTVisible in list flag
is_indexINTIndexed flag
sortINTDisplay order
optionsTEXTJSON option list
reference_tableVARCHARReference table
reference_columnVARCHARReference column
tenant_columnBOOLEANTenant flag
owner_columnBOOLEANOwner flag
index_typeVARCHARIndex type
index_groupVARCHARIndex group name
array_element_typeVARCHARArray element type

sys_meta_table_migration ​

FieldTypeDescription
idBIGINTPrimary key
table_idBIGINTRelated meta table
versionINTSchema version
change_typeVARCHAR(32)RENAME/DROP/ADD/ALTER_*
column_codeVARCHAR(64)Affected column
old_column_codeVARCHAR(64)Old column name on rename
old_type / new_typeVARCHAR(64)Type before/after
old_default / new_defaultVARCHAR(255)Default before/after
ddl_sqlTEXTGenerated DDL
statusVARCHAR(16)PENDING / EXECUTED
executed_atTIMESTAMPExecution time

API Endpoints ​

Tables are addressed by tableCode, never by database id (ids differ per environment). Every endpoint declares its own permission — the class-level ADMIN role alone is not enough.

MethodEndpointPermissionDescription
POST/admin/meta-tablemeta-table:listPaginated list of meta tables
GET/admin/meta-table/{tableCode}meta-table:listDetail with columns
POST/admin/meta-table/createmeta-table:addCreate a meta table
PUT/admin/meta-table/{tableCode}meta-table:editUpdate table and columns
PATCH/admin/meta-table/{tableCode}meta-table:editUpdate name / description / status only
POST/admin/meta-table/{tableCode}/schema-previewmeta-table:editPreview the schema diff before saving
GET/admin/meta-table/importable-tablesmeta-table:listPhysical tables that can be adopted
GET/admin/meta-table/import-preview?tableName=meta-table:listPreview the column mapping of an import
POST/admin/meta-table/importmeta-table:addAdopt an existing physical table
POST/admin/meta-table/{tableCode}/copymeta-table:addDuplicate a meta table; answers with the copy's tableCode
POST/admin/meta-table/{tableCode}/generatemeta-table:generateGenerate frontend/backend code
GET/admin/meta-table/{tableCode}/delete-checkmeta-table:removeRow count before deleting
DELETE/admin/meta-table/{tableCode}?force={false|true}meta-table:removeDelete meta table
GET/admin/meta-table/{tableCode}/migrationsmeta-table:listSchema migration history
GET/admin/meta-table/{tableCode}/export-migrationmeta-table:editExport migrations as Flyway SQL
POST/admin/meta-table/{tableCode}/datameta-table:listList rows of the dynamic table
POST/admin/meta-table/{tableCode}/data/createmeta-table:addInsert a row
PUT/admin/meta-table/{tableCode}/data/{dataId}meta-table:editUpdate a row
POST/admin/meta-table/{tableCode}/data/{dataId}/deletemeta-table:removeSoft-delete a row
GET/admin/meta-table/{tableCode}/export?format=EXCELmeta-table:exportExport table data
POST/admin/meta-table/{tableCode}/import?format=CSVmeta-table:addImport table data

Admin UI ​

The admin UI for meta tables is located at /src/views/meta-table/:

  • Meta table list — search by code/name, paginated table, create/edit/copy/delete/generate actions
  • Table designer — dialog form for editing table metadata and columns
  • Data management — open a meta table’s data grid to perform CRUD, search, import, and export
  • All buttons are controlled by meta-table:* permissions

Permissions ​

PermissionDescription
meta-table:listView meta tables, their detail and migration history; list rows
meta-table:queryQuery meta table data
meta-table:addCreate, copy or import a meta table; insert / import rows
meta-table:editUpdate a meta table, export migrations; update rows
meta-table:removeDelete a meta table (incl. delete-check); delete rows
meta-table:exportExport meta table data
meta-table:designDesign columns
meta-table:dataManage row data
meta-table:generateGenerate code scaffold

Typical Workflow ​

  1. Create a meta table with a unique code, name, and prefix.
  2. Add columns with types, constraints, and index settings.
  3. Save the design; the backend creates the physical table ({prefix}{table_code}). If a table with that name already exists, saving is refused — adopt existing tables with Import instead.
  4. Switch to the Data view to insert, edit, delete, import, or export rows.
  5. (Optional) Click Generate to produce a full-stack module scaffold.
  6. When requirements change, edit the table; the system computes and applies schema migration DDL.

Definition Files ​

Table definitions can also live in version control as project-definition/meta/<tableCode>.yaml: ./archforge meta export writes them from the DB, ./archforge meta import syncs files into the DB (dry-run unless --apply) and ./archforge meta check exits 1 on drift. With arch-forge.meta.source=file the files are the source of truth — they are applied at startup and designer / import writes are rejected.

The sync never runs DDL: physical tables come from Flyway or the designer. So meta check also fails — and meta import, the file-mode startup and the shadow check warn — when a registered table has no physical table.

Released under the MIT License.