定义模型
使用模型编辑器
打开 Dynamic model settings 视图。Visual 编辑器将当前模型呈现为由实体、属性、枚举、视图组成的连接表结构。可以使用对话框添加和编辑元素。
还可以随时切换到 Code 展现方式,直接检查或编辑原始的 YAML 定义;两种展现方式保持同步。
模型准备好后,点击 Apply。框架会将编辑后的模型与当前激活的模型进行比较,并打开 Dynamic model changes 视图,该视图用于在提交前显示将要应用的新内容。在 Model migrations 下列出每个单独的改动 — 例如创建实体、添加或删除属性、或添加唯一约束。如果有任何视图的变更,会列在 Changed views 下。
检查完列表的改动后,点击 Apply changes 确认。模型将进行验证,如果没问题,则变更生效并发布为新的模型版本。如果直接关闭 Dynamic model changes 视图,将丢弃待处理的变更,系统中激活的模型保持不变。
扩展静态实体
如需为应用程序中已存在的实体(例如 Customer 这样的静态实体)添加动态属性,在编辑器中选择该实体并向其添加属性。动态属性与实体自身的表分开存储,并在运行时与静态属性一样可用。
下面的示例中,为 Customer 添加了一个 taxId 字符串属性,长度为 20,带有必填标志、唯一性和验证规则:
- name: "Customer"
attributes:
- name: "taxId"
javaClass: "java.lang.String"
length: 20
required: true
unique: true
validation:
constraints:
- annotation: "Pattern"
parameters:
regexp: "^[A-Z0-9-]+$"
message:
en: "Tax ID can contain uppercase letters, digits and dashes only"
messages:
en: "Tax ID"
定义动态实体
动态实体是一个全新的实体,仅在运行时存在 — 即,在源代码中没有 Java 类或表。创建时,请在编辑器中添加一个与已有实体名称不同的新实体,然后定义其属性,并定义其视图(可选)。有关如何为动态实体生成视图和菜单项,请参见 视图和菜单项。
下面的示例定义了 LoyaltyLevel 动态实体,包含多个属性、验证约束以及几个生成的视图:
- name: "LoyaltyLevel"
resourceRoles:
read:
- "employee"
- "manager"
create:
- "manager"
update:
- "manager"
delete:
- "manager"
messages:
en: "Loyalty level"
de: "Treuestufe"
validation:
constraints:
- name: "discountDescriptionRequired"
target: "entity"
type: "expression"
evaluator: "spel"
expression: "discount == null || description != null"
attributes:
- "discount"
- "description"
path: "description"
groups:
- "UiCrossFieldChecks"
- "RestApiChecks"
message:
en: "Description is required when discount is specified"
attributes:
- name: "name"
javaClass: "java.lang.String"
instanceName: true
messages:
en: "Name"
- name: "description"
javaClass: "java.lang.String"
lob: true
messages:
en: "Description"
- name: "publicSummary"
javaClass: "java.lang.String"
calculated:
evaluator: "spel"
expression: "(name ?: '') + ': ' + (description ?: '')"
dependsOn:
- "name"
- "description"
messages:
en: "Public summary"
- name: "discount"
javaClass: "java.math.BigDecimal"
validation:
constraints:
- annotation: "DecimalMin"
parameters:
value: "0"
- annotation: "DecimalMax"
parameters:
value: "100"
messages:
en: "Discount"
views:
- type: "list"
viewId: "LoyaltyLevel.list"
viewRoute: "loyalty-levels"
viewTitle:
en: "Loyalty levels"
de: "Treuestufen"
templateParams:
includeProperties: ["name", "discount"]
excludeProperties: ["publicSummary"]
resourceRoles:
- "employee"
- "manager"
menuItem:
parentMenu: "application"
insertBefore: "Customer.list"
icon: "vaadin:star"
title:
en: "Loyalty levels"
resourceRoles:
- "employee"
- "manager"
descriptor:
template: "default"
- type: "detail"
viewId: "LoyaltyLevel.detail"
viewTitle:
en: "Loyalty level"
descriptor:
template: "default"
handlers:
- event: "initEntity"
expression: "entity.name = 'New level'"
- event: "valueChange"
componentId: "discountField"
expression: "components.setRequired('descriptionField', event.value != null)"
- type: "read"
viewId: "LoyaltyLevel.read"
viewTitle:
en: "Loyalty level"
属性
属性的不同类型由其定义的字段区分:数据类型属性、引用属性、集合属性或枚举属性。
数据类型属性
数据类型属性保存一个简单值,例如字符串、数字、日期或布尔值。通过选择类型进行定义,该类型存储为 javaClass。对于字符串和字节数组属性,还可以设置 length 并使用 lob 标记为对象存储。扩展静态实体 中显示的 taxId 属性就是一个数据类型属性。
有关支持类型的完整列表,请参见 支持的类型。
文件引用属性
Jmix 3.1+
FileRef 类型的属性保存 文件存储 中文件的引用。静态实体的动态属性和动态实体的属性都可以设置为此类型。值存储在字符串字段中。
在属性对话框中,选择 FileRef 类型。此类型下 Length 和 Lob 字段会被隐藏,因此该列的默认长度为 1024 个字符。在 YAML 中,javaClass 为 io.jmix.core.FileRef。
生成的视图以及从 模板 创建的 XML,都会使用 fileStorageUploadField 作为此类属性的 UI 组件。
实例名称
为属性勾选 Instance name,以将其值用作实体的 实例名称。一个实体只能有一个实例名称属性。如果另一个属性已经有了该标志,属性对话框会显示其名称,选择该标志会将其移动到当前属性。计算属性也可以作为实例名称。
引用属性
引用属性是关联另一个实体的单值链接属性。选择目标实体,在运行时该属性存储为关联实体实例的引用。
- name: "loyaltyLevel"
entityName: "LoyaltyLevel"
lookup:
type: "DROPDOWN"
actions:
- "entity_lookup"
- "entity_clear"
messages:
en: "Loyalty level"
查找字段
Jmix 3.1+
对于单值引用属性,可以选择使用特定的组件在生成的视图中选择实体实例。在属性对话框的 Lookup field type 和 Lookup actions 字段中进行设置。上面示例中的 loyaltyLevel 属性使用下拉列表。
Lookup field type 可以有以下值:
-
DROPDOWN– 一个 entityComboBox。条目在用户输入时按引用实体的实例名称加载,因此该类型适用于大型表。如果被引用实体的实例名称不基于字符串属性,则该控件改用查找视图。 -
VIEW– 一个 entityPicker,打开查找视图。如果未设置操作,则该控件使用标准查找和清除操作。
如果未设置类型,则使用默认组件。
Lookup actions 设置控件的按钮:entity_lookup、entity_open、entity_clear。
这些设置的作用类似于常规实体引用属性上的 @LookupField 注解。
枚举
动态枚举定义了一组固定的命名值,可由枚举属性使用。在编辑器中添加枚举,为其命名,并使用 id 和名称定义值。
enumerations:
- name: "CustomerGrade"
messages:
en: "Customer grade"
values:
- name: "PLATINUM"
id: "30"
messages:
en: "Platinum"
- name: "GOLD"
id: "20"
messages:
en: "Gold"
- name: "BRONZE"
id: "10"
messages:
en: "Bronze"
一个枚举中的所有值 id 必须是同一种类型:要么全部是整型 id(如上例所示),要么全是字符串 id。
计算型属性
计算型属性是只读的,在访问时根据表达式计算而非存储。使用 Spring Expression Language (SpEL) 编写表达式,并列出该属性 dependsOn 的属性,以便共同加载。
- name: "publicSummary"
javaClass: "java.lang.String"
calculated:
evaluator: "spel"
expression: "(name ?: '') + ': ' + (description ?: '')"
dependsOn:
- "name"
- "description"
messages:
en: "Public summary"
由于计算属性的值不存储在数据库中,计算型属性不能用于数据库级别的过滤或排序。
验证
可以为属性添加验证约束以限制其值的范围。每个约束都有一个类型(例如 Pattern、Size 或 DecimalMax)、参数(可选)和消息(可选)。
validation:
constraints:
- annotation: "Pattern"
parameters:
regexp: "^[A-Z0-9-]+$"
message:
en: "Tax ID can contain uppercase letters, digits and dashes only"
还支持实体级别的验证,实体级别的约束可以跨越多个属性。有关约束的完整列表和实体级别验证,请参见 YAML 参考。
唯一约束
如果要求属性值在所有实例中唯一,请将其标记为唯一。这是单属性唯一约束的简写形式:
unique: true
如果要求多个属性的组合唯一,需要在实体上定义复合唯一约束:
uniqueConstraints:
- name: "customerCountryTaxIdUnique"
attributes:
- "countryCode"
- "taxId"
message:
en: "Tax ID must be unique within a country"
唯一性由数据库约束强制执行。对于可软删除的静态实体,不支持唯一约束。
安全
可以使用资源角色限制对动态实体和属性的访问。授权是累加性质的 — 列出某个角色即授予其相应的访问权限,未授予的权限将被拒绝。使用的角色必须已存在于应用程序中;下面的示例使用了 employee 和 manager 角色。
在实体级别,授予 read、create、update 和 delete 访问权限。动态实体支持此功能:
resourceRoles:
read:
- "employee"
- "manager"
create:
- "manager"
update:
- "manager"
delete:
- "manager"
在属性级别,授予 view 和 modify 访问权限:
- name: "countryCode"
javaClass: "java.lang.String"
length: 2
resourceRoles:
view:
- "employee"
- "manager"
modify:
- "manager"
messages:
en: "Country code"
有关资源角色的更多信息,请参见 资源角色。
继承角色
Jmix 3.1+
为某个角色授予的访问权限也会在该角色的子角色中生效(无论层级多深),因此,有子角色的用户也会拥有父角色的权限。例如,如果模型授予 employee 角色读取权限,而 employee 是 manager 的父角色,那么仅拥有 manager 角色的用户会获得此权限。这个规则对以下类型的角色有效:
-
运行时创建的,有 父角色 的角色。
-
设计时创建的,扩展其他角色接口的角色接口。
同样的规则适用于模型中的所有 resourceRoles 角色的分配,包括对视图和菜单项的权限,以及 jmix.dynmodel.security.full-access-roles 属性中列出的角色。
角色作用域 仅针对分配给用户的角色进行检查。父角色的作用域不会检查。
模型配置的访问权限
扩展组件为 Dynamic model settings 视图提供以下资源角色。可以为需要配置模型的用户分配所需的角色。
| 角色名称 | 代码 | 访问权限 |
|---|---|---|
Dynamic model: view settings |
|
打开模型设置和模型历史。不能更改模型。 |
Dynamic model: modify settings |
|
完全访问权限。可以做任何更改,包括删除数据。 |
Dynamic model: modify settings without data loss |
|
仅支持不会丢失数据的更改。 |
不丢失数据的更改
Jmix 3.1+
"Dynamic model: modify settings without data loss" 角色允许用户扩展模型而不会产生丢失数据的风险。有此角色的用户可以在 Visual 和 Code 展示中编辑模型,并发布以下更改:新实体、属性、枚举和枚举值,以及对验证、视图、菜单项、消息和资源角色的更改。
用户不能发布可能丢失数据的更改:
-
移除已发布的实体或属性;
-
在 YAML 中重命名已发布的实体或属性,因为重命名会发布为移除和创建两个动作;
-
移除已发布的枚举;
-
从已发布的枚举中移除值。
在 Visual 展示中,已发布的内容不能被移除。在最后一次发布后添加的内容仍然可以被移除。如果编辑后的模型包含不允许的改动,Dynamic model changes 视图会列出这些改动,并且在移除这些改动之前无法发布模型。
该限制在服务器端检查。因此无法通过以另一种方式保存模型来规避,例如通过实体检查。