5.2. 数据模型
在本节中,我们将介绍如何使用应用程序的数据模型。
5.2.1. 使用实体
创建新实体
在项目树中选择 Data Model 部分或它下面的包,然后从右键菜单中选择 New > Entity。
出现 New CUBA Entity 对话框。在 Entity name 字段中输入实体类的名称,选择实体及其 ID 的类型。
Studio 将创建实体类,根据实体类型的不同在
persistence.xml
或metadata.xml
中进行注册。创建的类会在源代码编辑界面中打开。编辑界面底部会显示三个选项卡:Text 包含源码。
Designer 显示实体设计界面,可以使用图形界面配置实体及其属性,而不用编写 Java 代码。
DDL Preview 包含相应表及其参考约束的只读 DDL 代码。
创建实体属性
有多种方法可以向实体添加属性。
使用实体设计器的图形化界面:切换到 Designer 选项卡,单击 Attributes 表下方的 New 并填写 New Attribute 窗口中的必填字段。
Name 字段右边的地球仪按钮用于直接设置属性的用户友好名称。友好名称存储在
messages.properties
文件中,UI 组件默认使用这个文件来获取实体属性名称。如果为应用程序定义了多种语言,可以为所有语言指定本地化名称。使用从源码打开的独立窗口。在源码中将光标定位在最后一个字段下方,然后按下 Alt+Insert (Cmd+N)。在 Generate 菜单中,选择 Add Attribute。Studio 将显示 New Attribute 窗口,和从图形界面打开的一样。在代码编辑器的操作面板,也可以点击 Add attribute 按钮打开同样的窗口。
还可以手动编写属性字段,生成 getter 和 setter 方法,然后在 Generate 菜单中选择 JPA Annotations,这样可以使用默认参数添加 JPA 注解。
Studio 可以帮助将新增的属性添加到为此实体创建的 UI 界面。将光标定位到包含该属性的行,然后按 Alt+Enter(Option + Enter)或单击灯泡图标并选择 Add entity attribute to screens:
按 Enter 键,Studio 将打开一个对话框,其中包含使用了被编辑实体的界面列表。可以选择一个界面,Studio 会将该属性添加到此界面的相应 UI 组件中,例如添加到表格或表单中。
创建实例名称
可以用作另一个实体的引用属性的实体(例如 Customer
可以是 Order
的属性)需要一个模式(pattern)来生成实例的有意义的名称。此模式(pattern)由实体类上的 @NamePattern 注解定义。将光标定位在类名称上,即可在 Studio 中创建实体的名称模式,按 Alt+Enter(Option+Enter)并选择 Add name pattern(仅当实体没有 @NamePattern
注解时才显示此项):
按 Enter 键,Studio 将显示实体的所有属性列表。选择一个或多个属性,然后按 Enter 键。Studio 将在实体类上生成 @NamePattern
。
为新属性创建消息
手动创建新的实体属性时,其名称会高亮突出显示,以提醒在相应的消息包中创建用户友好的属性名称:
在突出显示的属性上点击 Alt+Enter(Option+Enter),然后选择 Create message in the message bundle:
移除实体
要移除实体,使用 Safe delete 选项查找并清理对实体的引用:
对实体的一些引用会被自动删除,比如在 persistence.xml
和 metadata.xml
文件中对实体的引用。如果存在对实体的引用,将会弹出一个对话框显示这些引用。 点击对话框上的 View Usages 按钮, 可在 Find 工具窗口中查看这些引用,这时可以根据情况点击 Cancel 或 Do Refactor 按钮。
5.2.2. 使用枚举
Studio 提供一组操作和可视化设计器帮助在应用程序中使用 枚举 。
枚举与实体一并在 CUBA 项目树的 Data Model 部分展示。
创建新的枚举
在项目树中选择 Data Model 或者其下面的一个包名,右键点击后选择 New > Enumeration 。
弹出 New CUBA Enumeration 对话框。在 Class 字段输入枚举类的名称,选择包名,Id 类型(推荐 String)。
Studio 将会创建枚举类,创建的类会在源码编辑器打开。编辑器的底部会显示两个标签页:
Text 包含源代码。
Designer 显示枚举设计器,这里可以使用图形界面配置枚举和值(常量),避免写 Java 代码。
使用 Values 表格和关联的按钮设置枚举值常量。
Name 列用于输入代码中使用的常量名称。这个名称可以后期修改而不影响数据库已经存在的数据。
Value 列用于输入枚举常量的 id。这是数据库保存的实际值。
地球按钮用于为选中的常量设置本地化名称(用户友好名称)。
设计器也提供将枚举的 Id type 从 String 修改为 Integer(或反过来)的功能。只需要打开枚举设计界面然后切换 Id type 的值。Studio 会自动在代码中对使用该枚举的地方做代码迁移。之后,您可以修改已有的枚举常量。注意,这种改动不会对数据库表格已存在的枚举值做改动,需要手动做数据迁移,比如可以通过一个数据库的更新脚本来实现。
5.2.3. 使用视图
要为实体创建新的 视图,请在项目树中选择实体,然后在右键菜单中点击 New > View。
视图设计界面会被打开。它包含以下字段:
Entity name - 要创建视图的实体的名称。
Name - 新视图的名称。
Extends - 内置或自定义视图,新视图会扩展其属性。任何实体都有三种内置视图:
_local
包含实体的所有本地属性(不引用其它实体的属性),_minimal
包含名称模式中列出的属性,_base
包括所有本地非系统属性和由@NamePattern
定义的属性(实际上是_minimal
+_local
)。
Configuration file - 用于存储此视图的 视图配置文件。默认情况下,Studio 在
global
模块中生成一个views.xml
文件。
当前实体的完整属性列表显示在字段下方的树中。可以通过选中属性前面的复选框来选择要包含在视图中的属性。
如果视图继承另一个视图,则会选中所有继承的属性,并禁用相对应的复选框。
如果选择引用属性,则右侧面板中会显示以下属性:
Entity - 引用的实体名称。
View - 加载引用实体的可选视图。建议使用已命名视图而不是临时指定视图属性,因为这样可以更容易地维护复杂视图。此外,即使指定了视图名称,仍然可以通过选择属性树中的复选框来添加视图中未包含的属性。
Fetch - 用于引用属性的可选设置,指定如何从数据库中获取相关实体。有关详细信息,请参阅 文档。
单击 OK 关闭设计界面后,可以在实体下的项目树中找到新视图:
如果双击项目树中的视图项,Studio 将在代码编辑界面中打开 views.xml
并将光标定位在视图定义上。代码编辑界面底部有两个选项卡:Text 和 Structure。后者显示此配置文件中定义的视图列表。
在 XML 中编辑视图定义时,使用 Ctrl+Space 自动完成属性名称:
要留意高亮突出显示的属性,它们很有可能不存在:
可以通过多种方式打开视图的图形设计界面:
在项目树中选择视图,然后单击 Edit View 右键菜单项。
将光标定位在配置文件代码中的视图元素上,按 Alt+Enter(Option+Enter),在弹出菜单中选择 Edit view,然后按 Enter 键。
切换到配置文件代码编辑界面的 Structure 选项卡,选择视图并点击 Edit 按钮。
5.2.4. 数据库迁移
Studio 能够创建 DDL 脚本,以使数据库架构与项目的数据模型保持同步。生成的脚本可以从 Studio 直接执行,也可以由 Gradle 任务 执行,还可以在应用程序本身 启动时 执行。
要生成 DDL 脚本,请在主菜单中单击 CUBA > Generate Database Scripts,或在项目树中选择 Data Model,然后在右键菜单中单击 Generate Database Scripts。
Studio 将打开 Database Scripts 窗口,窗口上有以下选项卡:
Updates
Updates 选项卡上显示出用来将数据库更新为数据模型当前状态的脚本。更新脚本保存在 modules/core/db/update
目录。这些脚本具有自动生成的名称,通过前缀定义执行顺序。包含 DROP 语句的脚本以红色突出显示。
可以通过单击 按钮添加任意脚本,添加的脚本后续将与自动生成的脚本一起保存并执行。
单击 按钮可以编辑或完全删除新生成的脚本。
如果单击 (排除),这时会两个选项:
将脚本移动到手动执行的脚本目录:
modules/core/db/update-manually
。然后当运行 Update database 时,脚本不会自动被执行,但可以在需要时手动运行它。此选项对于用于删除先前重命名为*__UNUSED
的表或列的脚本非常有用。排除脚本:排除的脚本不会保存到
modules/core/db/update
目录中,而是记录在项目文件夹中的studio-intellij.xml
文件中。再次生成脚本时,Studio 将忽略与排除脚本相对应的更改。这样就允许数据库架构和实体模型之间存在差异。例如,可能希望在对应项目实体的一个表中添加数据库字段,但不将其映射到实体属性。当 Studio 生成了从数据库中删除该字段的脚本时,只需将其排除,Studio 将不再生成同样的脚本。
Init Tables
执行 Create Database 时,Init Tables 脚本会在 Init constraints 和 Init data 脚本之前执行,并创建所有的表。开发人员可以编辑脚本,但要需要保留分隔表的注释。该脚本保存在 10.create-db.sql
文件中。
Init Constraints
Init Constraints 脚本在 Init tables 脚本之后执行,创建完整性约束。开发人员可以编辑该脚本,但需要保留分隔表的注释。该脚本保存在 20.create-db.sql
文件中。
Init Data
Init Data 脚本允许插入额外的数据或数据模型中不存在的数据结构信息。在初始化结束时执行。该脚本保存在 30.create-db.sql
文件中。
如果项目包含应用程序组件(扩展),但是此组件不为当前数据库提供 DDL 脚本,Studio 会为组件生成脚本,并在 Init {component} tables 和 Init {component} constraints 选项卡中显示。脚本分别保存在 01.{component}-create-db.sql
和 02.{component}-create-db.sql
文件中。
单击 Save and close 以保存所有生成的脚本。可以在 Project > Data Stores > Main Data Store 项目树部分中找到脚本。
要运行更新脚本,先停止应用程序服务器(如果在运行),然后从 CUBA 主菜单执行 Update Database。如果要使用初始化脚本从头开始重新创建数据库,请执行 Create Database。
5.2.5. 生成数据模型
Studio 允许为现有数据库创建数据模型和标准 UI 界面。单击 CUBA > Advanced > Generate Model 主菜单项或在项目树中选择 Project > Data Stores,然后在右键菜单中点击 Generate Model。如果有多个数据存储,Studio 会显示一个对话框,可以选择其一。
然后 Studio 打开 Generate Model from Database 向导。
步骤 1
这是模型生成向导的第一步。
可选步骤:单击 以设置创建的新实体的 Java 包位置以及实体的系统属性与数据库列的默认映射。
例如,如果数据库中的所有或大多数表包含 Modified
和 ModifiedBy
列,则可以将它们映射到被创建的实体的 Updatable.updateTs
和 Updatable.updatedBy
属性。在这种情况下,无需为每个表单独映射它们。
使用 Exclude columns from mapping 列表可以为所有表设置不需要映射到属性的列。
向导中会列出在项目数据模型中没有对应实体的表。可以使用上面的过滤器字段按名称查找表。
选择要映射到数据模型的表。某些表通过外键依赖于其它表,因此当选择这些表时,它所依赖的所有表也将被选中。如果取消选择一个表,则也会取消选择所有依赖它的表。
可以通过单击右侧的复选框来选择或取消选择所有可用表。
单击 Next。
步骤 2
在此步骤中,可以查看和编辑为所选表的自动生成的映射。
Status 列描述自动映射的结果:
OK - 自动映射成功,所有列都映射到新实体。
Join table - 识别出实体之间的关联,会被映射为多对多关系的连接表。
There are unmapped columns - 某些列无法映射到新实体。
New PK will be created - 该表没有主键。将创建一个新的 UUID 类型的主键。
Composite PK will be replaced - 该表具有复合主键,但没有其它表引用它。复合主键将被替换为 UUID 类型的主键。
Composite PK referenced by other tables - 该表有一个复合主键,一些表引用它。Studio 无法映射此类表。
Unsupported PK type - 该表具有不支持的主键类型。Studio 无法映射此类表。
Choose primary key for DB view - 它是一个数据库视图,应该选择适合作为实体标识符的一列或一组列。在这种情况下,单击 Choose PK 按钮并选择主键的列。
按钮允许重新运行所选表的自动映射。例如,可以切换到数据库 SQL 工具,对数据库架构进行一些更改,然后返回到向导并再次执行映射过程。
按钮打开一个包含映射详细信息的对话框窗口。在这里,可以更改实体名称和实体类要实现的系统接口。根据实现接口的不同,为了兼容 CUBA 的实体,会影响数据库列的创建数量。
当选中的是数据库视图并且需要选择用于实体标识符的列时,将显示 Choose PK 按钮而不是 Edit mapping。
通过单击 Back,可以返回到上一步以选择或取消选择表。
单击 Next 转到下一步。
步骤 3
在此步骤中,可以指定应为新实体创建哪些 UI 界面。
如果取消选中 Create standard screen 复选框,Studio 将不会为新实体生成 UI。
使用 In module 、Package 和 Menu 字段指定界面源代码的位置以及在主菜单中它们的显示位置。
使用 Standard screens 列中的下拉列表选择要生成的界面类型。
可以安全地跳过此步骤,并在完成模型生成过程后为实体生成 UI 界面。
单击 Next 转到下一步。
步骤 4
这是模型生成向导的最后一步。
Import scripts 表包含将在数据库上执行的脚本列表,查看这些脚本,以确认其符合要创建的实体。
在此之前,项目中不会创建任何内容,甚至也不会保存到磁盘中。Studio 实际上只会在点击 Run all scripts 或 Run script 时生成实体和界面并保存脚本。
可以在此页面上查看和编辑脚本,然后运行它们,或者仅保存脚本,稍后通过数据库管理工具来运行。导入脚本保存在 modules/core/db/import
目录中。
5.2.6. 集成自定义数据库
如 文档 中所述,框架允许使用 EclipseLink ORM 支持的任何 DBMS 作为项目数据库。Studio 可以帮助创建此类集成所需的文件。
在菜单中选择 CUBA > Advanced > Define Custom Database。
在打开的窗口中可以设置新自定义数据库的属性。根据这些属性,Studio 会针对数据库生成设计时和运行时的支撑代码。
DB type id - 用于
cuba.dbmsType
应用程序属性的数据库类型标识符。DB type name - 要在 Studio 中显示的数据库类型的用户友好名称。
单击 OK 后,Studio 会在 com.haulmont.cuba.core.sys.persistence
中生成 Java 类,并在项目的 com.haulmont.studio.db.{db_id}
包中生成 Groovy 类。自动生成的示例实现适用于 Microsoft SQLServer 数据库,需要适当地对其进行一些修改。
首先,修改 com.haulmont.studio.db.{db_id}.{db_id}DbProperties
类。当此类适配了新的数据库时,将能够在 Studio 中将项目切换到此数据库。重新打开项目在数据库类型下拉列表中查看新数据库。
要在运行时连接到新数据库,请修改 com.haulmont.cuba.core.sys.persistence
包的 {db_id}DbmsFeatures
和 {db_id}DbTypeConverter
类。{db_id}SequenceSupport
类仅用于生成整数标识符和唯一编号。
最后,修改 com.haulmont.studio.db.{db_id}.{db_id}DdlGenerator
类,以便在需要时可以由 Studio 正确生成 init 和 update 数据库脚本。如果不需要为此数据库生成 DDL 脚本,可跳过此步骤。
如果将自定义数据库用作主数据存储,则在生成数据库脚本时,Studio 将为所有应用程序组件(包括 CUBA)创建 init 脚本。这些脚本不包含一些必须的初始化数据,因此必须将以下内容添加到项目的 Init data 脚本中(30.create-db.sql
):
insert into SEC_GROUP (ID, CREATE_TS, VERSION, NAME, PARENT_ID)
values ('0fa2b1a5-1d68-4d69-9fbd-dff348347f93', current_timestamp, 0, 'Company', null)^
insert into SEC_USER (ID, CREATE_TS, VERSION, LOGIN, LOGIN_LC, PASSWORD, NAME, GROUP_ID, ACTIVE)
values ('60885987-1b61-4247-94c7-dff348347f93', current_timestamp, 0, 'admin', 'admin',
'cc2229d1b8a052423d9e1c9ef0113b850086586a',
'Administrator', '0fa2b1a5-1d68-4d69-9fbd-dff348347f93', 1)^
insert into SEC_USER (ID, CREATE_TS, VERSION, LOGIN, LOGIN_LC, PASSWORD, NAME, GROUP_ID, ACTIVE)
values ('a405db59-e674-4f63-8afe-269dda788fe8', current_timestamp, 0, 'anonymous', 'anonymous', null,
'Anonymous', '0fa2b1a5-1d68-4d69-9fbd-dff348347f93', 1)^