模型优先开发
当前仓库的默认开发顺序不是“先写 Java”,而是:
先模型,再生成,再补保留层代码。
默认流程
- 修改
model/*.orm.xml。 - 首次建模块时使用
nop-cli gen生成骨架。 - 后续变更优先用
./mvnw触发再生成与构建。 - 只在非生成文件中写定制逻辑。
首次生成骨架
nop-cli gen model/{appName}.orm.xml -t=/nop/templates/orm -o=.
这个命令的用途是生成标准业务骨架,而不是日常每次改模型都重跑一遍。
后续模型变更
默认优先在项目根目录执行:
./mvnw clean install -T 1C
模型加载后的 Java 初始化
不是所有派生逻辑都适合继续放在 XML / imp / codegen 里。
当 source 模型在加载完成后,还需要:
- 回填父上下文
- 构建运行期索引
- 规范化类名/类型名
- 递归初始化子模型
优先考虑在模型类中实现 INeedInit,把这些逻辑收口到 Java init()。
标准 XDSL 解析链会自动调用 INeedInit.init();但如果你是手工 new、手工 merge、手工转换模型,通常需要自己补 init()。
详见:./model-init-and-ineedinit.md
实体菜单图标约定
当 ORM 实体会参与标准后台页面与 action-auth.xml 生成时,可以在 entity 上声明 ext:icon:
<entity ... ext:icon="user-round">
规则如下:
ext:icon配置在model/*.orm.xml的entity节点。- 值优先使用 kebab-case 的 Lucide 图标名,例如
user-round、building-2、git-branch。 - 该值会传播到生成的
xmeta根节点ext:icon。 - ORM web 模板生成
_*.action-auth.xml时,会优先使用objMeta['ext:icon']作为菜单图标;未配置时回退到默认图标。 - 当前仓库约定下,source
model/*.orm.xml中参与标准菜单生成的实体都应显式设置ext:icon;不要再把 entity icon 留给默认值兜底。
这个约定只影响基于 ORM 标准模板生成的菜单资源;手工维护的 *.action-auth.xml 覆盖文件需要单独调整。
模块菜单图标约定
当 ORM 模块会参与标准后台页面与 action-auth.xml 生成时,可以在根 <orm> 上声明 ext:icon:
<orm ... ext:icon="shield">
规则如下:
- 根
ext:icon配置在 sourcemodel/*.orm.xml的<orm>节点。 {appName}-meta会生成/{moduleId}/model/_module-meta.json,并生成module-meta.json通过x:extends继承它。{appName}-web的 ORM 页面模板通过loadDeltaJson("/{moduleId}/model/module-meta.json")读取模块级 meta,而不是直接依赖/{moduleId}/orm/app.orm.xml。- ORM web 模板生成
_*.action-auth.xml时,会优先使用moduleMeta.icon作为 TOPM 图标;未配置时回退到ion:grid-outline。 entity ext:icon的传播链保持不变,仍然用于生成 SUBM 图标。- 当前仓库约定下,每个 source
model/*.orm.xml都应显式设置根<orm ext:icon>;不要依赖ion:grid-outline作为模块菜单的长期默认图标。
这个约定的目的,是让 *-web 只依赖 *-meta 输出,不要求 *-web 一定能访问 *-dao 下的 ORM 产物。
手写菜单图标约定
如果在 source *.action-auth.xml 中手写 resourceType="TOPM" 或 resourceType="SUBM" 的菜单资源,也必须显式提供 icon。
规则如下:
- 这条规则适用于 source
*.action-auth.xml,不适用于 generated_*.action-auth.xml。 - 只要资源显式声明为
TOPM或SUBM,就不要省略icon。 - 手写菜单不能假设生成链会自动替它补 icon;需要在 source 文件中自行给出。
如果只想理解生成职责,可以按模块顺序看:
{appName}-codegen刷新项目级生成产物。{appName}-dao接收 ORM、Entity、接口等结果。{appName}-meta生成 XMeta 与 i18n。{appName}-meta也会生成模块级module-meta.json,供 web 层读取模块图标等元数据。{appName}-web基于 XMeta 与module-meta.json生成页面相关文件;如果 web 侧 i18n 依赖第一轮生成出的action-auth.xml,则需要在precompile2中二次生成。{appName}-service/{appName}-app接收对应派生产物并参与构建。
真实链路
model/{app}.orm.xml
-> {app}-codegen/postcompile/gen-orm.xgen
-> {app}-dao / {app}-service / {app}-meta / {app}-web
-> {app}-meta/precompile/gen-meta.xgen
-> {app}-meta/postcompile/gen-i18n.xgen
-> {app}-web/precompile/gen-page.xgen
-> {app}-web/precompile2/gen-i18n.xgen (当 web i18n 依赖最终 action-auth.xml 时)
AI 的默认修改顺序
| 目标 | 默认修改位置 |
|---|---|
| 表、字段、关系、字典 | model/*.orm.xml |
| 页面或元数据派生能力 | 先看 model / xmeta / xbiz |
| 升级友好的产品定制 | Delta |
| 实体辅助方法 | 保留层 Entity |
| 业务接口逻辑 | BizModel / Processor |
什么时候不要直接写 Java
以下情况先不要手写服务或 DAO:
- 只是新增字段、校验、字典、关系。
- 标准 CRUD 已经够用。
- 页面和 API 结构可以由模型派生。
- 只是要在现有产品上做差量定制。
不能手改的典型文件
_gen/目录。_app.orm.xml。_service.beans.xml。_*.xbiz、_*.view.xml、_*.java。
ORM 列类型与 JSON 组件
VARCHAR precision → 实际 SQL 类型的自动选择
框架在 SqlDataTypeMapping 中根据 dialect 配置自动将 stdSqlType="VARCHAR" + precision 映射为最合适的数据库类型(以 MySQL 为例):
| precision 范围 | 实际 SQL 类型 |
|---|---|
| ≤ 16383 | VARCHAR(N) |
| 16384 ~ 65535 | TEXT |
| 65536 ~ 16777215 | MEDIUMTEXT |
| 更大或无限制 | LONGTEXT(即 CLOB) |
H2 等 embedded 数据库没有中间类型,超过 VARCHAR 上限直接映射为 CLOB。
设计建议:按实际数据大小设置 precision,框架会自动选类型。不要为了避免截断而设置过大的值——过大会导致 MySQL 用 TEXT/MEDIUMTEXT 而非 VARCHAR,影响索引和查询性能。
stdDomain="json" 自动生成 JsonOrmComponent
当 ORM 模型的 column 满足以下任一条件时(由 orm-gen.xlib 的 JsonComponentSupport 在元编程阶段处理):
stdDomain="json"tagSet包含"json"
框架自动为该字段生成一个 JsonOrmComponent,命名为 {fieldName}Component。代码中可直接操作 JSON 对象:
// 不需要手动序列化
entity.getExtDataComponent().set_jsonValue(Map.of("key", "value"));
// 读取时自动反序列化
Object data = entity.getExtDataComponent().get_jsonValue();
同时生成的 XMeta 中会自动设置 graphql:jsonComponentProp,GraphQL API 直接暴露结构化 JSON 而非原始字符串。
类似的自动组件还有:stdDomain="xml" → XmlOrmComponent,stdDomain="file" / "file-list" → OrmFileComponent / OrmFileListComponent。
常见误区
- 先手写 Entity / DAO / Biz 接口,再回头补模型。
- 把
*-meta误解为直接生成全部 service / web 代码。 - 只改生成物,不回到源模型。
- 改了模型却没有触发上游生成链。
相关文档
../01-repo-map/domain-module-pattern.md../03-runbooks/create-new-entity.md../03-runbooks/add-field-and-validation.md../03-runbooks/change-model-and-regenerate.md../04-reference/source-anchors.md