介绍Model相关元数据,以及对应代码注解方式。大家还是可以通读并练习每种不同的使用方式,这个是oinone的设计精华所在。当您不知道如何配置模型、字段、模型间的关系、以及枚举都可以到这里找到。
一、模型元数据
安装与更新
使用@Model.model来配置模型的不可变更编码。模型一旦安装,无法在对该模型编码值进行修改,之后的模型配置更新会依据该编码进行查找并更新;如果仍然修改该注解的配置值,则系统会将该模型识别为新模型,存储模型会创建新的数据库表,而原表将会rename为废弃表。
如果模型配置了@Base注解,表明在studio中该模型配置不可变更;如果字段配置了@Base注解,表明在studio中该字段配置不可变更。
注解配置
模型类必需使用@Model注解来标识当前类为模型类。
可以使用@Model.model、@Fun注解模型的模型编码(也表示命名空间),先取@Model.model注解值,若为空则取@Fun注解值,若皆为空则取全限定类名。
模型元信息
模型的priority,当展示模型定义列表时,使用priority配置来对模型进行排序。
模型的ordering,使用ordering属性来配置该模型的数据列表的默认排序。
模型元信息继承形式:
- 不继承(N)
- 同编码以子模型为准(C)
- 同编码以父模型为准(P)
- 父子需保持一致,子模型可缺省(P=C)
注意:模型上配置的索引和唯一索引不会继承,所以需要在子模型重新定义。数据表的表名、表备注和表编码最终以父模型配置为准;扩展继承父子模型字段编码一致时,数据表字段定义以父模型配置为准。
名称 |
描述 |
抽象继承 |
同表继承 |
代理继承 |
多表继承 |
基本信息 |
|
|
|
|
|
displayName |
显示名称 |
N |
N |
N |
N |
summary |
描述摘要 |
N |
N |
N |
N |
label |
数据标题 |
N |
N |
N |
N |
check |
模型校验方法 |
N |
N |
N |
N |
rule |
模型校验表达式 |
N |
N |
N |
N |
模型编码 |
|
|
|
|
|
model |
模型编码 |
N |
N |
N |
N |
高级特性 |
|
|
|
|
|
name |
技术名称 |
N |
N |
N |
N |
table |
逻辑数据表名 |
N |
P=C |
P=C |
N |
type |
模型类型 |
N |
N |
N |
N |
chain |
是否是链式模型 |
N |
N |
N |
N |
index |
索引 |
N |
N |
N |
N |
unique |
唯一索引 |
N |
N |
N |
N |
managed |
需要数据管理器 |
N |
N |
N |
N |
priority |
优先级,默认100 |
N |
N |
N |
N |
ordering |
模型查询数据排序 |
N |
N |
N |
N |
relationship |
是否是多对多关系模型 |
N |
N |
N |
N |
inherited |
多重继承 |
N |
N |
N |
N |
unInheritedFields |
不从父类继承的字段 |
N |
N |
N |
N |
unInheritedFunctions |
不从父类继承的函数 |
N |
N |
N |
N |
高级特性-数据源 |
|
|
|
|
|
dsKey |
数据源 |
N |
P=C |
P=C |
N |
高级特性-持久化 |
|
|
|
|
|
logicDelete |
是否逻辑删除 |
P |
P |
P |
N |
logicDeleteColumn |
逻辑删除字段 |
P |
P |
P |
N |
logicDeleteValue |
逻辑删除状态值 |
P |
P |
P |
N |
logicNotDeleteValue |
非逻辑删除状态值 |
P |
P |
P |
N |
underCamel |
字段是否驼峰下划线映射 |
P |
P |
P |
N |
capitalMode |
字段是否大小写映射 |
P |
P |
P |
N |
高级特性-序列生成配置 |
|
|
|
|
|
sequence |
配置编码 |
C |
C |
C |
N |
prefix |
前缀 |
C |
C |
C |
N |
suffix |
后缀 |
C |
C |
C |
N |
separator |
分隔符 |
C |
C |
C |
N |
size |
序列长度 |
C |
C |
C |
N |
step |
序列步长 |
C |
C |
C |
N |
initial |
初始值 |
C |
C |
C |
N |
format |
序列格式化 |
C |
C |
C |
N |
高级特性-关联关系(或逻辑外键) |
|
|
|
|
|
unique |
外键值是否唯一 |
C |
C |
C |
N |
foreignKey |
外键名称 |
C |
C |
C |
N |
relationFields |
关系字段列表 |
C |
C |
C |
N |
references |
关联模型 |
C |
C |
C |
N |
referenceFields |
关联字段列表 |
C |
C |
C |
N |
limit |
关系数量限制 |
C |
C |
C |
N |
pageSize |
查询每页个数 |
C |
C |
C |
N |
domainSize |
模型筛选可选项每页个数 |
C |
C |
C |
N |
domain |
模型筛选,前端可选项 |
C |
C |
C |
N |
onUpdate |
更新关联操作 |
C |
C |
C |
N |
onDelete |
删除关联操作 |
C |
C |
C |
N |
静态配置 |
|
|
|
|
|
Static |
静态元数据模型 |
N |
N |
N |
N |
表4-1-6-1 模型元信息
字段定义继承形式
名称 |
描述 |
抽象继承 |
同表继承 |
代理继承 |
多表继承 |
字段定义 |
字段定义 |
C |
C |
C |
C |
表4-1-6-2 字段定义继承形式
模型约束
主键约束
每个模型都可以配置自身的主键列表,也可以不配置主键。主键值不可缺省,可以索引到模型所对应数据表中唯一的一条记录。
外键约束
模型与模型之间的关联关系可以配置外键约束来约束关联关系之间数据的变更行为。
校验约束
模型可以配置校验函数对该模型的数据进行校验,存储数据时,校验数据是否合法合规。
二、字段元数据
模型字段描述的是实体的特征属性。模型与字段之间的关联关系由Model的model与Field的model进行关联。ModelField继承关系抽象类Relation。
使用@Field注解来描述模型的字段。如果未配置字段类型,系统会根据Java代码的字段声明类型来自动获取业务类型。建议配置displayName属性来描述字段在前端的显示名称。可以使用defaultValue配置字段的默认值。
元数据注解说明

图4-1-6-1 元数据注解说明
安装与更新
使用@Field.field来配置字段的不可变更编码。字段一旦安装,无法在对该字段编码值进行修改,之后的字段配置更新会依据该编码进行查找并更新;如果仍然修改该注解的配置值,则系统会将该字段识别为新字段,存储模型会创建新的数据库表字段,而原字段将会rename为废弃字段。
基础配置
不可变更字段
使用immutable属性来描述该字段前后端都无法进行更新操作,系统会忽略不可变更字段的更新操作。
自动生成编码的字段
可以使用@Field.Sequence注解在字段上配置编码生成规则,为编码为空的字段自动生成编码。
详见3.3.5【模型编码生成器】
字段的序列化与反序列化
使用@Field注解的serialize属性来配置非字符串类型属性的序列化与反序列化方式,最终会以序列化后的字符串持久化到存储中。
详见3.3.7【字段之序列化方式】
前端默认配置
可以使用@Field注解中的以下属性来配置前端的默认视觉与交互规则,也可以在前端设置覆盖以下配置。
- required,是否必填
- invisible,是否不可见
- priority,字段优先级,列表的列使用该属性进行排序
字段类型
类型系统由基本类型、复合(组件)类型、引用类型和关系类型四种类型系统构成。通过类型系统来决定应用程序、数据库和前端视觉视图是如何进行交互,数据及数据间关系如何处理的。
基本类型
业务类型 |
Java类型 |
数据库类型 |
规则说明 |
BINARY |
Byte Byte[] |
TINYINT BLOB |
二进制类型,不推荐使用 |
INTEGER |
INTEGER
Short
Integer
Long
BigInteger |
smallint
int
bigint
decimal(size,0) |
整数, 包括整数(10-11位有效数字)、长整数(19-20位有效数字)和大整数(超过19位)。
【数据库规则】:默认使用int;如果size小于6则使用smallint;如果size超过6则使用int;如果size超过10位数字,即大于11(包含符号位),则使用长整数bigint;如果size超过19位数字,即大于20(包含符号位),则使用大数decimal。若未配置size,则按Java类型推测。
【前端交互规则】:整数使用Number类型,长整数和大整数前后端协议使用字符串类型。 |
二进制类型,不推荐使用 |
FLOAT |
Float
Double
BigDecimal |
float(M,D)
double(M,D)
decimal(M,D) |
浮点数, 包括单精度浮点数(7-8位有效数字)、双精度浮点数(15-16位有效数字)和大数(超过15位)。
【数据库规则】:默认使用单精度浮点数float;
如果size超过7位数字,即大于等于8,则使用双精度浮点数double;如果size超过15位数字,即大于等于16,则使用大数decimal。若未配置size,则按Java类型推测。
【前端交互规则】:单精度浮点数float和双精度浮点数double使用Number类型(因为都使用IEEE754协议64位进行存储),大数前后端协议使用字符串类型。 |
BOOLEAN |
Boolean |
tinyint(1) |
布尔类型,值为1,true(真)或0,false(假) |
ENUM |
Enum |
varchar(size) |
【前端交互规则】:可选项从ModelField的options字段获取,options字段值为字段指定数据字典子集的JSON序列化字符串。前后端传递的是可选项的name,数据库存储使用可选项的value。multi属性为true,则使用多选控件;multi属性为false,则使用单元控件 |
STRING |
String |
varchar(size) |
字符串,size为长度限制默认值参考,前端可以view中覆盖该配置 |
TEXT |
String |
text |
多行文本,编辑态组件为多行文本框,长度限制为配置项size值 |
HTML |
String |
text |
富文本编辑器 |
DATETIME |
java.util.Date
java.sql.Timestamp |
datetime(fraction)
timestamp(fraction) |
日期时间类型
【数据库规则】:日期和时间的组合,
时间格式为 YYYY-MM-DD HH:MM:SS[.fraction],默认精确到秒,在默认的秒精确度上,可以带小数,最多带6位小数,即可以精确到 microseconds (6 digits) precision。可以通过设置fraction来设置精确小数位数,最终存储在字段的decimal属性上。
【前端交互规则】:前端默认使用日期时间控件,根据日期时间类型格式化格式format格式化日期时间 |
YEAR |
java.util.Date |
year |
日期时间类型
年份类型
日期类型
【数据库规则】:默认“YYYY”格式表示的日期值
【前端交互规则】:前端默认使用年份控件,根据日期类型格式化格式format格式化日期 |
DATE |
java.util.Date
java.sql.Date |
date
date |
date
date 日期类型
【数据库规则】:默认“YYYY-MM-DD”格式表示的日期值
【前端交互规则】:前端默认使用日期控件,根据日期类型格式化格式format格式化日期 |
TIME |
java.util.Date
java.sql.Time |
time(fraction)
time(fraction) |
time(fraction)
time(fraction) 时间类型
【数据库规则】:默认“HH:MM:SS”格式表示的时间值
【前端交互规则】:前端默认使用时间控件,根据日期类型格式化格式format格式化日期 |
表4-1-6-3 基本类型
#### 复合类型
业务类型 |
Java类型 |
数据库类型 |
规则说明 |
BINARY |
ByteByte[] |
TINYINTBLOB |
二进制类型,不推荐使用 |
INTEGER |
ShortIntegerLongBigInteger |
smallintintbigintdecimal(size,0) |
整数, 包括整数(10-11位有效数字)、长整数(19-20位有效数字)和大整数(超过19位)。【数据库规则】:默认使用int;如果size小于6则使用smallint;如果size超过6则使用int;如果size超过10位数字,即大于11(包含符号位),则使用长整数bigint;如果size超过19位数字,即大于20(包含符号位),则使用大数decimal。若未配置size,则按Java类型推测。【前端交互规则】:整数使用Number类型,长整数和大整数前后端协议使用字符串类型。 |
FLOAT |
FloatDoubleBigDecimal |
float(M,D)double(M,D)decimal(M,D) |
浮点数,?包括单精度浮点数(7-8位有效数字)、双精度浮点数(15-16位有效数字)和大数(超过15位)。【数据库规则】:默认使用单精度浮点数float;如果size超过7位数字,即大于等于8,则使用双精度浮点数double;如果size超过15位数字,即大于等于16,则使用大数decimal。若未配置size,则按Java类型推测。【前端交互规则】:单精度浮点数float和双精度浮点数double使用Number类型(因为都使用IEEE754协议64位进行存储),大数前后端协议使用字符串类型。 |
BOOLEAN |
Boolean |
tinyint(1) |
布尔类型,值为1,true(真)或0,false(假) |
ENUM |
Enum |
与数据字典指定基本类型一致 |
【前端交互规则】:可选项从ModelField的options字段获取,options字段值为字段指定数据字典子集的JSON序列化字符串。前后端传递的是可选项的name,数据库存储使用可选项的value。multi属性为true,则使用多选控件;multi属性为false,则使用单元控件 |
STRING |
String |
varchar(size) |
字符串,size为长度限制默认值参考,前端可以view中覆盖该配置 |
TEXT |
String |
text |
多行文本,编辑态组件为多行文本框,长度限制为配置项size值 |
HTML |
String |
text |
富文本编辑器 |
DATETIME |
java.util.Datejava.sql.Timestamp |
datetime(fraction)timestamp(fraction) |
日期时间类型【数据库规则】:日期和时间的组合,时间格式为?YYYY-MM-DD HH:MM:SS[.fraction],默认精确到秒,在默认的秒精确度上,可以带小数,最多带6位小数,即可以精确到?microseconds (6 digits) precision。可以通过设置fraction来设置精确小数位数,最终存储在字段的decimal属性上。【前端交互规则】:前端默认使用日期时间控件,根据日期时间类型格式化格式format格式化日期时间 |
YEAR |
java.util.Date |
year |
年份类型日期类型【数据库规则】:默认“YYYY”格式表示的日期值【前端交互规则】:前端默认使用年份控件,根据日期类型格式化格式format格式化日期 |
DATE |
java.util.Datejava.sql.Date |
datedate |
日期类型【数据库规则】:默认“YYYY-MM-DD”格式表示的日期值【前端交互规则】:前端默认使用日期控件,根据日期类型格式化格式format格式化日期 |
TIME |
java.util.Datejava.sql.Time |
time(fraction)time(fraction) |
时间类型【数据库规则】:默认“HH:MM:SS”格式表示的时间值【前端交互规则】:前端默认使用时间控件,根据日期类型格式化格式format格式化日期 |
表4-1-6-3 基本类型
复合类型
业务类型 |
Java类型 |
数据库类型 |
规则说明 |
MONEY |
BigDecimal |
decimal(M,D) |
金额,前端使用金额控件,可以使用currency设置币种字段 |
表4-1-6-4 复合类型
#### 引用类型
业务类型 |
Java类型 |
数据库类型 |
规则说明 |
RELATED |
基本类型或关系类型 |
不存储或varchar、text |
引用字段【数据库规则】:点表达式最后一级对应的字段类型;数据库字段值默认为Java字段的序列化值,默认使用JSON序列化【前端交互规则】:点表达式最后一级对应的字段控件类型 |
表4-1-6-5 引用类型
#### 关系类型
业务类型 |
Java类型 |
数据库类型 |
规则说明 |
O2O |
模型/DataMap |
不存储或varchar、text |
一对一关系 |
M2O |
模型/DataMap |
不存储或varchar、text |
多对一关系 |
O2M |
List<模型/DataMap> |
不存储或varchar、text |
一对多关系 |
M2M |
List<模型/DataMap> |
不存储或varchar、text |
多对多关系 |
表4-1-6-6 关系类型
多值字段或者关系字段需要存储,默认使用JSON格式序列化。多值字段数据库字段类型默认为varchar(1024);关系字段数据库字段类型默认为text。
#### 类型默认推断
M代表精度,即有效长度(总位数), D代表标度,即小数点后的位数,fraction为时间秒以下精度。multi表示该字段为多值字段。
Java类型 |
Field注解 |
推断ttype |
推断配置 |
推断数据库配置 |
Byte |
@Field |
BINARY |
无 |
blob |
String |
@Field |
STRING |
size=128 |
varchar(128) |
List |
@Field |
STRING |
size=1024,multi=true |
varchar(1024) |
Map |
@Field |
STRING |
size=1024 |
varchar(1024) |
Short |
@Field |
INTEGER |
M=5 |
smallint(6) |
Integer |
@Field |
INTEGER |
M=10 |
integer(11) |
Long |
@Field |
INTEGER |
M=19 |
bigint(20) |
BigInteger |
@Field |
INTEGER |
M=64 |
decimal(64,0) |
Float |
@Field |
FLOAT |
M=7,D=2 |
float(7,2) |
Double |
@Field |
FLOAT |
M=15,D=4 |
double(15, 4) |
BigDecimal |
@Field |
FLOAT |
M=64,D=6 |
decimal(64,6) |
Boolean |
@Field |
BOOLEAN |
无 |
tinyint(1) |
java.util.Date |
@Field |
DATETIME |
fraction=0 |
datetime |
java.util.Date |
@Field.Date(type=DateTypeEnum.YEAR) |
YEAR |
无 |
year |
java.util.Date |
@Field.Date(type=DateTypeEnum.DATE) |
DATE |
无 |
date |
java.util.Date |
@Field.Date(type=DateTypeEnum.TIME) |
TIME |
fraction=0 |
time |
java.sql.Timestamp |
@Field |
DATETIME |
fraction=0 |
timestamp |
java.sql.Date |
@Field |
DATE |
无 |
date |
java.sql.Time |
@Field |
TIME |
fraction=0 |
time |
Long |
@Field.Date |
DATETIME |
fraction=0 |
datetime |
enum implementsIEnum |
@Field |
ENUM |
无 |
根据枚举value类型 |
primitive type |
@Field.Enum(dictionary=数据字典编码) |
ENUM |
无 |
根据枚举value类型 |
List |
@Field.Enum(dictionary=数据字典编码) |
ENUM |
multi=true |
varchar(512) |
模型类 |
@Field.Relation |
M2O |
无 |
text |
DataMap |
@Field.Relation |
M2O |
无 |
text |
List<模型类> |
@Field.Relation |
O2M |
multi=true |
text |
List |
@Field.Relation |
O2M |
multi=true |
text |
表4-1-6-7 类型默认推断
### 字段约束
#### 主键
可以使用Yaml或者@Model.Advanced的keyGenerator属性来配置模型主键的自动生成规则,AUTO_INCREMENT或者分布式ID。如果不配置,将不会自动生成主键值。
#### 逻辑外键约束
在创建关联关系字段的时候,可以使用@Field.Relation注解的onUpdate和onDelete属性指定在删除模型或更新模型关系字段值时,对关联模型进行的相应操作。操作包括RESTRICT、NO ACTION、SET NULL和CASCADE,默认值为RESTRICT。
- RESTRICT是指模型与关联模型有关联记录的情况下,引擎会阻止模型关系字段的更新或删除模型记录;
- NO ACTION是指不作约束(这里与数据库约束的定义不相同);
- CASCADE表示在更新模型关系字段或者删除模型时,级联更新关联模型对应记录的关联字段值或者级联删除关联模型对应记录;
- SET NULL则是表示在更新模型关系字段或者删除模型的时候,关联模型的对应关联字段将被SET NULL(该字段值允许为null的情况下,若不允许为null,则引擎阻止对模型的操作)。
#### 通用校验约束
字段业务类型 |
size |
limit |
decimal |
mime |
min |
max |
BINARY |
文件类型 |
最小比特位 |
最大比特位 |
INTEGER |
有效数字 |
最小值 |
最大值 |
FLOAT |
有效数字 |
小数位数 |
最小值 |
最大值 |
BOOLEAN |
ENUM |
存储字符数 |
多选最多数量 |
STRING |
存储字符数 |
字符数 |
字符数 |
TEXT |
字符数 |
字符数 |
HTML |
字符数 |
字符数 |
MONEY |
有效数字 |
小数位数 |
最小值 |
最大值 |
RELATED |
表4-1-6-8 通用校验约束(表一)
字段业务类型 |
fraction |
format |
min |
max |
DATETIME |
时间精度 |
时间格式 |
最早日期时间 |
最晚日期时间 |
YEAR |
时间格式 |
最早年份 |
最晚年份 |
DATE |
时间格式 |
最早日期 |
最晚日期 |
TIME |
时间精度 |
时间格式 |
最早时间 |
最晚时间 |
表4-1-6-9 通用校验约束(表二)
字段业务类型 |
size |
domainSize |
limit |
pageSize |
RELATED |
存储字符数(若序列化存储) |
O2O |
存储字符数(若序列化存储) |
可选项每页个数 |
M2O |
存储字符数(若序列化存储) |
可选项每页个数 |
O2M |
存储字符数(若序列化存储) |
可选项每页个数 |
关系数量限制 |
查询每页个数 |
M2M |
存储字符数(若序列化存储) |
可选项每页个数 |
关系数量限制 |
查询每页个数 |
表4-1-6-10 通用校验约束(表三)
在模型或字段上配置check函数,则处理前端请求时会进行校验约束。也可以调用模型上的check函数进行编程式校验。
#### 默认值约束
字段默认值defaultValue可以是基本类型或者关系类型的序列化值。时间类型可以使用format来格式化时间表达式或者使用长整数来设置默认值。枚举类型使用枚举项值value来设置默认值。如果需要进行复杂的计算请使用模型的construct构造函数来配置解决。
#### 唯一约束
将字段或者模型上配置unique唯一索引,可以为模型或字段添加唯一约束。
#### 可选项约束
使用枚举定义字段的可选项值,可以为字段提供可选项约束功能。
### 关系字段
关联关系用于描述模型间的关联方式:
- 多对一关系,主要用于明确从属关系
- 一对多关系,主要用于明确从属关系
- 多对多关系,主要用于弱依赖关系的处理,提供中间模型进行关联关系的操作
- 一对一关系,主要用于多表继承和行内合并数据

图4-1-6-2 关系字段
#### 名词解释
关联关系比较重要的名词解释如下:
- 关联关系:使用relation表示,模型间的关联方式的一种描述,包括关联关系类型、关联关系双边的模型和关联关系的读写
- 关联关系字段:业务类型ttype为O2O、O2M、M2O或M2M的字段
- 关联模型:使用references表示,自身模型关联的模型
- 关联字段:使用referenceFields表示,关联模型的字段,表示关联模型的哪些字段与自身模型的哪些字段建立关系
- 关系模型:自身模型
- 关系字段:使用relationFields表示,自身模型的字段,表示自身模型的哪些字段与关联模型的哪些字段建立关系
- 中间模型,使用through表示,只有多对多存在中间模型,模型的relationship=true
#### 关联关系的默认视图
- 一对多默认视图,编辑态在行内是下拉多选,在详情是选项卡表格;展示态在行内是折叠面板表格,在详情是选项卡表格
- 多对一默认视图,编辑态在行内是下拉单选,在详情是下拉单选;展示态在行内是文字,在详情是文字
- 多对多默认视图,编辑态在行内是下拉多选,在详情是选项卡表格;展示态在行内是折叠面板表格,在详情是选项卡表格
- 一对一默认视图,编辑态在行内是平铺,在详情是分组;展示态在行内是平铺,在详情是分组
在后端研发的使用上所有关联关系和引用的处理都限制在本模型,即平台至多处理到当前模型的字段,不再继续依据关联关系和引用处理关联模型。但是可以手动调用模型上的链式方法fieldQuery、fieldCreate和fieldUpdate来完成关联关系的查询与更新操作。
使用O2M或者M2M关联关系关联的临时模型没有分页查询操作。
#### 关联关系的配置
可以使用@Field.Relation注解的relationFields、referenceFields、references和through来配置关联关系。
relationFields与referenceFields为存储关联关系的一一映射字段列表。
如果relationFields缺省,一对多或者多对多关系的relationFields默认为模型主键;一对一或者多对一关系的relationFields默认为关联关系字段名加上首字母大写的主键名拼接而成的字符串。如果有多个主键,则relationFields和referenceFields也对应有多个字段。
如果配置了relationFields,但referenceFields缺省,则referenceFields与relationFields字段名一致。
一对多关系的referenceFields必填。如果referenceFields缺省,多对多,多对一或者一对一关系的referenceFields默认为主键。
关系类型 |
缺省关系字段默认值 |
缺省关联字段默认值 |
一对多 |
默认为关系模型的pk |
默认为关系模型名+关系模型的pk;如果关系另一端的多对一字段名不是关系模型名,则需明确指定,使两端关系字段与关联字段对应 |
一对一 |
默认为关联关系字段名+关系模型的pk |
默认为关联模型的pk |
多对一 |
默认为关联关系字段名+关系模型的pk |
默认为关联模型的pk |
多对多 |
默认为关系模型的pk |
默认为关联模型的pk |
表4-1-6-11 关联关系的配置
多对多使用through来指定中间模型的模型编码,如果指定模型编码的中间模型不存在,系统会根据through自动生成中间模型,中间模型的默认字段为与两端模型关联的关系字段。与关系模型关联的关系字段名称为关系模型名称加上关系模型的主键拼接而成的字符串;与关联模型关联的多对一字段名称为关联模型名称加上关联模型主键拼接而成的字段串。如果与关系模型关联的关系字段名称和与关联模型关联的多对一字段名称冲突,需要使用throughRelationFields和throughReferenceFields明确配置指定字段名称解决冲突。
系统根据模块的依赖关系,自动生成的中间模型将生成在先加载的建立多对多关系的关系模型所在模块。
#### 读写关联关系字段
默认关联关系字段的store属性为false,relationStore为true。若设置关联关系字段relationStore属性为true,则会为关联关系字段生成关系字段用于存储关联关系。若设置关联关系字段store属性为true,则存储时序列化字段值存储到数据库中,查询时从数据库中反序列化得到字段值。字段类型为varchar且长度为128,如果需要改变字段长度,可以使用@Field.Advanced的columnDefinition设置。当store属性为false时,则字段值为关联关系查询得到的结果。如果store为false且relationStore为false,则只能对字段进行赋值来设置字段值。
#### 关联操作
调用数据管理器的API不会触发关联操作,需要调用fieldQuery和fieldSave方法进行关联模型的关联操作。
前端的查询接口会根据GraphQL协议进行关联查询。
前端的新增和更新接口默认会存储当前模型的关联关系字段和递归新增和更新一对多关系的关联关系字段。更新接口会检查当前模型的逻辑外键约束。可以调用模型的ignore方法或设置模型数据的ignore属性来改变递归深度,避免循环操作。
前端的删除接口会默认删除当前模型数据和根据级联配置进行当前模型的关联关系字段的关联操作。删除接口会检查当前模型的逻辑外键约束。可以调用模型的ignore方法或设置模型数据的ignore属性来改变递归深度,避免循环操作。
#### 关联数据分页
可以使用关系字段配置中分页数量pageSize来限定关联查询的返回结果数量。可选项可以使用domainSize来限定可选项返回结果数量,由前端从字段元数据中获取并设置为可选项查询分页数量限制。
#### 反转关系
一对多关联关系可以设置inverse为true反转关系,反转关系后关联关系存储在一对多关系中“一”这一端。
#### 引用字段
引用字段可以通过与其他字段建立引用关系来获取数据。
当引用字段的store属性为true时,则字段值为存储的字段值,数据存储时将被引用字段值存储到数据存储中(unset掉被引用字段,则直接存储引用字段值);当store属性为false时,则数据为被引用字段的字段值且不会存储。
4.1.7 函数之元数据详解
上一篇
2024年5月23日
4.1.5 模型之持久层配置
下一篇
2024年5月23日
相关推荐
-
消息中间件是在分布式开发中常见的一种技术手段,用于模块间的解耦、异步处理、数据最终一致等场景。 一、介绍 oinone对开源的RocketMQ进行了封装,是平台提供的一种较为简单的使用方式,并非是对RocketMQ进行的功能扩展。同时也伴随着两个非常至关重要的目的: 适配不同企业对RocketMQ的不同版本选择,不至于改上层业务代码。目前已经适配RocketMQ的开源版本和阿里云版本。 下个版本会对API进行升级支持不同类型MQ,以适配不同企业对MQ的不同要求,应对一些企业客户已经对MQ进行技术选择 对协议头进行扩展:如多租户的封装,saas模式中为了共用MQ基础资源,需要在消息头中加入必要租户信息。 二、使用准备 demo工程默认已经依赖消息,这里只是做介绍无需大家额外操作,大家可以用maven依赖树命令查看引用关系。 依赖包 增加对pamirs-connectors-event的依赖 <dependency> <groupId>pro.shushi.pamirs.framework</groupId> <artifactId>pamirs-connectors-event</artifactId> </dependency> 图4-1-21-1 分布式消息的依赖包 相关功能引入 增加模型、触发器都依赖MQ <!– 增强模型 –> <!– 增强模型 –> <dependency> <groupId>pro.shushi.pamirs.core</groupId> <artifactId>pamirs-channel</artifactId> </dependency> <!– 触发器 api –> <dependency> <groupId>pro.shushi.pamirs.core</groupId> <artifactId>pamirs-trigger-api</artifactId> </dependency> <!– 触发器 core –> <dependency> <groupId>pro.shushi.pamirs.core</groupId> <artifactId>pamirs-trigger-core</artifactId> </dependency> 图4-1-21-2 增加模型、触发器都依赖MQ yml配置文件参考 详见4.1.1【模块之yml文件结构详解】的“pamirs.event”部分。 三、使用说明 发送消息(NotifyProducer) 概述 NotifyProducer是Pamirs Event中所有生产者的基本API,它仅仅定义了消息发送的基本行为,例如生产者自身的属性,启动和停止,当前状态,以及消息发送方法。它本身并不决定消息如何发送,而是根据具体的实现确定其功能。 目前仅实现了RocketMQProducer,你可以使用下面介绍的方法轻松使用这些功能。 使用方法 Notify注解方式 使用示例 @Component public class DemoProducer { @Notify(topic = "test", tags = "model") public DemoModel sendModel() { return new DemoModel(); } @Notify(topic = "test", tags = "dto") public DemoDTO sendDTO() { return new DemoDTO(); } } 图4-1-21-3 Notify注解方式使用示例 解释说明 使用Component注解方式注册Spring Bean。 Notify注解指定topic和tags。 topic和tags对应NotifyEvent中的topic和tags。 RocketMQProducer方法调用 使用示例 @Component public class SendRocketMQMessage { @Autowired private RocketMQProducer rocketMQProducer; /** * 发送普通消息 */ public void sendNormalMessage() { rocketMQProducer.send(new NotifyEvent("test", "model", new DemoModel())); rocketMQProducer.send(new NotifyEvent("test", "dto", new DemoDTO())); } /** * 发送有序消息 */ public void sendOrderlyMessage() { DemoModel data = new DemoModel(); data.setAge(10); rocketMQProducer.send(new NotifyEvent("test", "model", data) .setQueueSelector((queueSize, event) -> { DemoModel body = (DemoModel) event.getBody(); return body.getAge() % queueSize; })); } /** * 发送事务消息 */ public void sendTransactionMessage() { rocketMQProducer.send(new NotifyEvent("test", "model", new DemoModel()) .setIsTransaction(true) .setGroup("demoTransactionListener")); } } 图4-1-21-4 RocketMQProducer方法调用…
-
界面设计器组件管理页面添加组件 进入组件的元件管理页面 点击“添加元件” 设计元件的属性 这里以是否“显示清除按钮”作为自定义属性从左侧拖入到中间设计区域,然后发布 点击“返回组件” 鼠标悬浮到卡片的更多按钮的图标,弹出下拉弹出“低无一体”的按钮 在弹窗内点击“生成SDK”的按钮 生成完成后,点击“下载模板工程” 解压模板工程kunlun-sdk.zip 解压后先查看README.MD,了解一下工程运行要点,可以先运行 npm i 安装依赖 再看kunlun-plugin目录下已经有生成好的组件对应的ts和vue文件 下面在vue文件内增加自定义代码,可以运行 npm run dev 在开发模式下调试看效果 <template> <div class="my-form-string-input"> <oio-input :value="realValue" @update:value="change" > <template #prepend>MyPrepend</template> </oio-input> </div> </template> <script lang="ts"> import { defineComponent, ref } from 'vue'; import { OioInput } from '@kunlun/vue-ui-antd'; export default defineComponent({ name: 'customField1', components: { OioInput }, props: { value: { type: String }, change: { type: Function }, }, setup(props) { const realValue = ref<string | null | undefined>(props.value); return { realValue }; } }); </script> <style lang="scss"> .my-form-string-input { border: 1px solid red; } </style> 确定改好代码后运行 npm run build,生成上传所需的js和css文件 可以看到 kunlun-plugin目录下多出了dist目录,我们需要的是 kunlun-plugin.umd.js 和 kunlun-plugin.css 这2个文件 再次回到组件的“低无一体”管理弹窗页面,上传上面生成的js和css文件,并点击“确定”保存,到这里我们的组件就新增完成了。 下面我们再到页面设计器的页面中使用上面设计的组件(这里的表单页面是提前准备好的,这里就不介绍如何新建表单页面了) 将左侧组件库拉直最底部,可以看到刚刚新建的组件,将其拖至中间设计区域,我们可以看到自定义组件的展示结果跟刚刚的代码是对应上的(ps: 如果样式未生效,请刷新页面查看,因为刚刚上传的js和css文件在页面初始加载的时候才会导入进来,刚刚上传的动作未导入新上传的代码文件),再次点击设计区域中的自定义组件,可以看到右侧属性设置面板也出现了元件设计时拖入的属性。 最后再去运行时的页面查看效果,与代码逻辑一致!
-
本文会介绍不同类型模型以及其简单的应用场景,方便大家理解不同类型模型的用途 模型分为元模型和业务模型。元数据是指描述应用程序运行所必需的数据、规则和逻辑的数据集;元模型是指用于描述内核元数据的一套模式集合;业务模型是指用于描述业务应用元数据的一套模式集合。 元模型分为模块域、模型域和函数域三个域。域的划分规则是根据元模型定义数据关联关系的离散性来判断,离散程度越小越聚集到一个域。在4.1.4【模块元数据详解】一文中介绍的ModuleDefinition就是元模型。而我们在开发中涉及的就是业务模型 一、模型类型 抽象模型:往往是提供公共能力和字段的模型,它本身不会直接用于构建协议和基础设施(如表结构等)。 传输模型:用于表现层和应用层之间的数据交互,本身不会存储,没有默认的数据管理器,只有数据构造器。 存储模型:存储模型用于定义数据表结构和数据的增删改查(数据管理器)功能,是直接与连接器进行交互的数据容器。 代理模型:用于代理存储模型的数据管理器能力的同时,扩展出非存储数据信息的交互功能的模型。 二、模型定义种类 模型定义就是模型描述,不同定义类型,代表计算描述模型的元数据的规则不同 静态模型定义:模型元数据不持久化、不进行模型定义的计算(默认值、主键、继承、关联关系) 静态计算模型定义:模型元数据不持久化但初始化时进行模型定义计算获得最终的模型定义 动态模型定义:模型元数据持久化且初始化时进行模型定义计算获得最终的模型定义 静态模型定义需要使用@Model.Static进行注解;静态计算模型定义使用@Model.Static(compute=true)进行注解;动态模型定义不注解@Model.Static注解。 三、安装与更新 使用@Model.model来配置模型的不可变更编码。模型一旦安装,无法在对该模型编码值进行修改,之后的模型配置更新会依据该编码进行查找并更新;如果仍然修改该注解的配置值,则系统会将该模型识别为新模型,存储模型会创建新的数据库表,而原表将会rename为废弃表。 如果模型配置了@Base注解,表明在【oinone的设计器】中该模型配置不可变更;如果字段配置了@Base注解,表明在【oinone的设计器】中该字段配置不可变更。 四、基础配置 模型基类 所有的模型都需要继承以下模型中的一种,来表明模型的类型,同时继承以下模型的默认数据管理器(详见3.3.3模型的数据管理器一节)。 继承BaseModel,构建存储模型,默认无id属性。 继承BaseRelation,构建多对多关系模型,默认无id属性。 继承TransientModel,构建临时模型(传输模型),临时模型没有数据管理器,也没有id属性。 继承EnhanceModel,构建数据源为ElasticSearch的增强模型。 快捷继承 继承IdModel,构建主键为id的模型。继承IdModel的模型会数据管理器会增加queryById方法(根据id查询单条记录) 继承CodeModel,构建带有唯一编码code的主键为id的模型。可以使用@Model.Code注解配置编码生成规则。也可以直接覆盖CodeModel的generateCode方法或者自定义新增的前置扩展点自定义编码生成逻辑。继承CodeModel的模型会数据管理器会增加queryByCode方法(根据唯一编码查询单条记录) 继承VersionModel,构建带有乐观锁,唯一编码code且主键为id的模型。 继承IdRelation,构建主键为id的多对多关系模型。 模型继承关系图 图3-3-2-1 模型继承关系图 AbstractModel抽象基类是包含createDate创建时间、writeDate更新时间、createUid创建用户ID、writeUid更新用户ID、aggs聚合结果和activePks批量主键列表等基础字段的抽象模型。 TransientModel传输模型抽象基类是所有传输模型的基类,传输模型不存储,没有数据管理器。 TransientRelation传输关系模型是所有传输关系模型的基类,传输关系模型不存储,用于承载多对多关系,没有数据管理器。 BaseModel存储模型基类提供数据管理器功能,数据模型主键可以不是ID。 IdModel带id模型抽象基类,在BaseModel数据管理器基础之上提供根据ID查询、更新、删除数据的功能。 BaseRelation关系模型抽象基类用于承载多对多关系,是多对多关系的中间模型,数据模型主键可以不是ID。 IdRelation带id关系模型抽象基类,在BaseRelation数据管理器基础之上提供根据ID查询、更新、删除数据的功能。 CodeModel带code模型抽象基类,提供按配置生成业务唯一编码功能,根据code查询、更新、删除数据的功能。 EnhanceModel增强模型,提供全文检索能力。此模型会在4.1.25【框架之搜索引擎】一文中展开介绍。 五、抽象模型(举例) 抽象模型本身不会直接用于构建协议和基础设施(如表结构等),而是通过继承的机制供子模型复用其字段和函数。子模型可以是所有类型的模型。 比如demo模块要管理的一些公共模型字段,我们可以建一个AbstractDemoIdModel和AbstractDemoCodeModel,demo模块中的实体模型就可以继承它们。我们来为demo模块的模型统一增加一个数据状态这么一个字段,用做数据的生效与失效管理。 Step1 引入DataStatusEnum类 pamirs-demo-api的pom.xml包增加依赖,便于引入DataStatusEnum类,当然也可以自己建,这里只是oinone提供了统一的数据记录状态的枚举,以及相应的通用方法,这边就直接引入 <dependency> <groupId>pro.shushi.pamirs.core</groupId> <artifactId>pamirs-core-common</artifactId> </dependency> 图3-3-2-2 引入通用类库 Step2 修改DemoModule DataStatusEnum枚举类本身也会做为数据字典,以元数据的方式被管理起来,当一个模块依赖另一个模块的元数据相关对象,则需要改模块的模块依赖定义。为DemoModule增加CommonModule的依赖注解 package pro.shushi.pamirs.demo.api; import org.springframework.stereotype.Component; import pro.shushi.pamirs.boot.base.ux.annotation.action.UxRoute; import pro.shushi.pamirs.boot.base.ux.annotation.navigator.UxHomepage; import pro.shushi.pamirs.core.common.CommonModule; import pro.shushi.pamirs.demo.api.model.PetShop; import pro.shushi.pamirs.meta.annotation.Module; import pro.shushi.pamirs.meta.base.PamirsModule; import pro.shushi.pamirs.meta.common.constants.ModuleConstants; @Component @Module( name = DemoModule.MODULE_NAME, displayName = "oinoneDemo工程", version = "1.0.0", dependencies = {ModuleConstants.MODULE_BASE, CommonModule.MODULE_MODULE} ) @Module.module(DemoModule.MODULE_MODULE) @Module.Advanced(selfBuilt = true, application = true) @UxHomepage(@UxRoute(PetShop.MODEL_MODEL)) public class DemoModule implements PamirsModule { public static final String MODULE_MODULE = "demo_core"; public static final String MODULE_NAME = "DemoCore"; @Override public String[] packagePrefix() { return new String[]{ "pro.shushi.pamirs.demo"}; } } 图3-3-2-3 定义模块依赖 Step3 新建AbstractDemoCodeModel和AbstractDemoIdModel 并新增AbstractDemoIdModel和AbstractDemoCodeModel分别继承IdModel和CodeModel,实现IDataStatus接口不是必须的,刚好DataStatus有配套的通用逻辑,暂时也先加进去,具体使用会在本文的【代理模型】这段介绍 package pro.shushi.pamirs.demo.api.model; import pro.shushi.pamirs.core.common.behavior.IDataStatus; import pro.shushi.pamirs.core.common.enmu.DataStatusEnum; import pro.shushi.pamirs.meta.annotation.Field; import pro.shushi.pamirs.meta.annotation.Model; import pro.shushi.pamirs.meta.annotation.sys.Base; import pro.shushi.pamirs.meta.base.common.CodeModel; import pro.shushi.pamirs.meta.enmu.ModelTypeEnum; @Base @Model.model(AbstractDemoCodeModel.MODEL_MODEL) @Model.Advanced(type = ModelTypeEnum.ABSTRACT) @Model(displayName = "AbstractDemoCodeModel") public abstract class AbstractDemoCodeModel extends CodeModel implements IDataStatus { public static final String MODEL_MODEL="demo.AbstractDemoCodeModel"; @Base @Field.Enum @Field(displayName = "数据状态",defaultValue = "DISABLED",required =…
-
一、使用场景 在碰到大数据量并且需要全文检索的场景,我们在分布式架构中基本会架设ElasticSearch来作为一个常规解决方案。在oinone体系中增强模型就是应对这类场景,其背后也是整合了ElasticSearch。 二、整体介绍 oinone与es整合设计图 图4-1-25-1 Oinone与es整合设计图 基础环境安装 Canal安装 详见4.1.10【函数之触发与定时】一文 修改Canal配置并重启 新增Canal的实例【destinaion: pamirs】,监听分表模型的binlog【filter: demo.demo_core_sharding_model……】用于增量同步 pamirs: middleware: data-source: jdbc-url: jdbc:mysql://localhost:3306/canal_tsdb?useUnicode=true&characterEncoding=utf-8&verifyServerCertificate=false&useSSL=false&requireSSL=false driver-class-name: com.mysql.cj.jdbc.Driver username: root password: oinone canal: ip: 127.0.0.1 port: 1111 metricsPort: 1112 zkClusters: – 127.0.0.1:2181 destinations: – destinaion: pamirschangedata name: pamirschangedata desc: pamirschangedata slaveId: 1235 filter: demo\.demo_core_pet_talent dbUserName: root dbPassword: oinone memoryStorageBufferSize: 65536 topic: CHANGE_DATA_EVENT_TOPIC dynamicTopic: false dbs: – { address: 127.0.0.1, port: 3306 } – destinaion: pamirs id: 1234 name: pamirs desc: pamirs slaveId: 1234 filter: demo\.demo_core_sharding_model_0,demo\.demo_core_sharding_model_1,demo\.demo_core_sharding_model_2,demo\.demo_core_sharding_model_3,demo\.demo_core_sharding_model_4,demo\.demo_core_sharding_model_5,demo\.demo_core_sharding_model_6,demo\.demo_core_sharding_model_7 dbUserName: root dbPassword: oinone memoryStorageBufferSize: 65536 topic: BINLOG_EVENT_TOPIC dynamicTopic: false dbs: – { address: 127.0.0.1, port: 3306 } tsdb: enable: true jdbcUrl: "jdbc:mysql://127.0.0.1:3306/canal_tsdb" userName: root password: oinone mq: rocketmq rocketmq: namesrv: 127.0.0.1:9876 retryTimesWhenSendFailed: 5 dubbo: application: name: canal-server version: 1.0.0 registry: address: zookeeper://127.0.0.1:2181 protocol: name: dubbo port: 20881 scan: base-packages: pro.shushi server: address: 0.0.0.0 port: 10010 sessionTimeout: 3600 图4-1-25-2 修改Canal配置并重启 ES安装 下载安装包官方下载地址,也可以直接下载elasticsearch-8.4.1-darwin-x86_64.tar.gz.txt(361.7 MB),下载后去除后缀.txt,然后解压文件 替换安装目录/config下的[elasticsearch.yml](elasticsearch)(1 KB),主要是文件中追加了三个配置 xpack.security.enabled: false xpack.security.http.ssl.enabled: false xpack.security.transport.ssl.enabled: false 图4-1-25-3 elasticsearc.yml追加三个配置 启动 a. 导入环境变量(ES运行时需要JDK18及以上版本JDK运行环境, ES安装包中包含了一个JDK18版本) # export JAVA_HOME=/Users/oinone/Documents/oinone/es/elasticsearch-8.4.1/jdk.app/Contents/Home/ export JAVA_HOME=ES解压安装目录/jdk.app/Contents/Home/ 图4-1-25-4 导入环境变量 b. 运行ES ## nohup /Users/oinone/Documents/oinone/es/elasticsearch-8.4.1/bin/elasticsearch >> $TMPDIR/elastic.log 2>&1 & nohup ES安装目录/bin/elasticsearch >> $TMPDIR/elastic.log 2>&1 & 图4-1-25-5 运行ES 停止ES lsof…
-
在前面章节中我们也涉及到菜单,因为菜单我们模块就是地图、导航,没有地图、导航就无法畅游模块并进行相关业务操作。在3.3.4【模块的继承】一文关于多表继承的内容就有涉及到菜单的初始化,本文将展开介绍初始化Menu的两种方式分别是:注解式、数据初始化式。 注解式(举例) Step1 分析现有菜单注解 用@UxMenus声明DemoMenus为菜单初始化入口,同时该类在DemoModule配置扫描路径中,那么通过DemoMenus初始化的菜单都挂在demo_core这个模块上。 如果采用这种模式,建议同一个模块的菜单都只配置在一处 package pro.shushi.pamirs.demo.core.init; import pro.shushi.pamirs.boot.base.constants.ViewActionConstants; import pro.shushi.pamirs.boot.base.ux.annotation.action.UxRoute; import pro.shushi.pamirs.boot.base.ux.annotation.navigator.UxMenu; import pro.shushi.pamirs.boot.base.ux.annotation.navigator.UxMenus; import pro.shushi.pamirs.demo.api.model.*; import pro.shushi.pamirs.demo.api.proxy.PetShopProxy; import pro.shushi.pamirs.demo.api.proxy.PetShopProxyA; import pro.shushi.pamirs.demo.api.proxy.PetShopProxyB; @UxMenus public class DemoMenus implements ViewActionConstants { @UxMenu("商店管理")@UxRoute(PetShopProxy.MODEL_MODEL) class PetShopProxyMenu{} @UxMenu("商店管理A")@UxRoute(PetShopProxyA.MODEL_MODEL) class PetShopProxyAMenu{} @UxMenu("商店管理B")@UxRoute(PetShopProxyB.MODEL_MODEL) class PetShopProxyBMenu{} @UxMenu("商品管理")@UxRoute(PetItem.MODEL_MODEL) class ItemMenu{} @UxMenu("宠狗商品")@UxRoute(PetDogItem.MODEL_MODEL) class DogItemMenu{} @UxMenu("萌猫商品")@UxRoute(PetCatItem.MODEL_MODEL) class CatItemMenu{} @UxMenu("宠物品种")@UxRoute(PetType.MODEL_MODEL) class PetTypeMenu{} @UxMenu("萌猫品种")@UxRoute(PetCatType.MODEL_MODEL) class CatTypeMenu{} @UxMenu("宠狗品种")@UxRoute(PetDogType.MODEL_MODEL) class DogTypeMenu{} @UxMenu("宠物达人")@UxRoute(PetTalent.MODEL_MODEL) class PetTalentMenu{} } 图3-5-1-1 菜单注解 Step2 改造现有菜单注解 菜单的层级关系通过@UxMenu的嵌套进行描述 菜单点击效果有三种分别对应不同的Action的类型,关于Action的类型详见3.5.3【Action的类型】一文。 通过@UxRoute定义一个与菜单绑定的viewAction,@UxMenu("创建商店")@UxRoute(value = PetShop.MODEL_MODEL,viewName = "redirectCreatePage",viewType = ViewTypeEnum.FORM),其中viewName代表视图的name(其默认值为redirectListPage,也就是跳转到列表也),value代码视图所属模型的编码,viewType代表view类型(其默认值为ViewTypeEnum.TABLE) @UxServer定义一个与菜单绑定的serverAction,@UxMenu("UxServer")@UxServer(model = PetCatItem.MODEL_MODEL,name = "uxServer") ,其中name代表serverAction的name,model或value代码serverAction所属模型的编码 @UxLink定义一个与菜单绑定的UrlAction,@UxMenu("Oinone官网")@UxLink(value = "http://www.oinone.top”,openType= ActionTargetEnum.OPEN_WINDOW) ,其中value为跳转url,openType为打开方式默认为ActionTargetEnum.ROUTER,打开方式有以下几种 ROUTER("router", "页面路由", "页面路由") DIALOG("dialog", "页面弹窗", "页面弹窗") DRAWER("drawer", "打开抽屉", "打开抽屉") OPEN_WINDOW("openWindow", "打开新窗口", "打开新窗口") 配合菜单演示,PetCatItemAction增加一个uxServer的ServerAction package pro.shushi.pamirs.demo.core.action; ……包引用 @Model.model(PetCatItem.MODEL_MODEL) @Component public class PetCatItemAction extends DataStatusBehavior<PetCatItem> { ……省略其他代码 @Action(displayName = "uxServer") public PetCatItem uxServer(PetCatItem data){ PamirsSession.getMessageHub().info("uxServer"); return data; } } 图3-5-1-2 示例代码 新的菜单初始化代码如下 package pro.shushi.pamirs.demo.core.init; import pro.shushi.pamirs.boot.base.constants.ViewActionConstants; import pro.shushi.pamirs.boot.base.enmu.ActionTargetEnum; import pro.shushi.pamirs.boot.base.ux.annotation.action.UxLink; import pro.shushi.pamirs.boot.base.ux.annotation.action.UxRoute; import pro.shushi.pamirs.boot.base.ux.annotation.action.UxServer; import pro.shushi.pamirs.boot.base.ux.annotation.navigator.UxMenu; import pro.shushi.pamirs.boot.base.ux.annotation.navigator.UxMenus; import pro.shushi.pamirs.demo.api.model.*; import pro.shushi.pamirs.demo.api.proxy.PetShopProxy; import pro.shushi.pamirs.demo.api.proxy.PetShopProxyA; import pro.shushi.pamirs.demo.api.proxy.PetShopProxyB; import pro.shushi.pamirs.meta.enmu.ViewTypeEnum; @UxMenus public class DemoMenus implements ViewActionConstants { @UxMenu("商店") class ShopMenu{ @UxMenu("UxServer")@UxServer(model = PetCatItem.MODEL_MODEL,name = "uxServer") class ShopSayHelloMenu{ } @UxMenu("创建商店")@UxRoute(value = PetShop.MODEL_MODEL,viewName = "redirectCreatePage",viewType = ViewTypeEnum.FORM) class ShopCreateMenu{ }…