5.2. 数据模型

在本节中,我们将介绍如何使用应用程序的数据模型。

5.2.1. 使用实体

  • 创建新实体
    • 在项目树中选择 Data Model 部分或它下面的包,然后从右键菜单中选择 New > Entity
    • 出现 New CUBA Entity 对话框。在 Entity name 字段中输入实体类的名称,选择实体及其 ID 的类型。
    • Studio 将创建实体类,根据实体类型的不同在 persistence.xmlmetadata.xml 中进行注册。创建的类会在源代码编辑界面中打开。编辑界面底部会显示三个选项卡:
      • Text 包含源码。
      • Designer 显示实体设计界面,可以使用图形界面配置实体及其属性,而不用编写 Java 代码。
      • DDL Preview 包含相应表及其参考约束的只读 DDL 代码。
  • 创建实体属性
  • 有多种方法可以向实体添加属性。
  • 使用实体设计器的图形化界面:切换到 Designer 选项卡,单击 Attributes 表下方的 New 并填写 New Attribute 窗口中的必填字段。

Name 字段旁边的按钮用于直接设置属性的用户友好名称。友好名称存储在 messages.properties 文件中,UI 组件默认使用这个文件来获取实体属性名称。如果为应用程序定义了多种语言,可以为所有语言指定本地化名称。

attribute l10n

  • 使用从源码打开的独立窗口。在源码中将光标定位在最后一个字段下方,然后按下 Alt+Insert (Cmd+N)。在 Generate 菜单中,选择 Add Attribute。Studio 将显示 New Attribute 窗口,和从图形界面打开的一样。

new attribute 2

  • 还可以手动编写属性字段,生成 getter 和 setter 方法,然后在 Generate 菜单中选择 JPA Annotations,这样可以使用默认参数添加 JPA 注解。

Studio 可以帮助将新增的属性添加到为此实体创建的 UI 界面。将光标定位到包含该属性的行,然后按 Alt+Enter(Option + Enter)或单击灯泡图标并选择 Add entity attribute to screens

add attribute to screens

按 Enter 键,Studio 将打开一个对话框,其中包含使用了被编辑实体的界面列表。可以选择一个界面,Studio 会将该属性添加到此界面的相应 UI 组件中,例如添加到表格或表单中。

  • 创建实例名称

可以用作另一个实体的引用属性的实体(例如 Customer 可以是 Order 的属性)需要一个模式(pattern)来生成实例的有意义的名称。此模式(pattern)由实体类上的 @NamePattern 注解定义。将光标定位在类代码中的任何位置,即可在 Studio 中创建实体的名称模式,按 Alt+Enter(Option+Enter)并选择 Add name pattern(仅当实体没有 @NamePattern 注解时才显示此项):

create name pattern

按 Enter 键,Studio 将显示实体的所有属性列表。选择一个或多个属性,然后按 Enter 键。Studio 将在实体类上生成 @NamePattern

  • 为新属性创建消息

手动创建新的实体属性时,其名称会高亮突出显示,以提醒在相应的消息包中创建用户友好的属性名称:

create message 1

在突出显示的属性上点击 Alt+Enter(Option+Enter),然后选择 Create message in the message bundle

create message

  • 移除实体

要移除实体,使用 Safe delete 选项查找并清理对实体的引用:

remove entity

对实体的一些引用会被自动删除,比如在 persistence.xmlmetadata.xml 文件中对实体的引用。如果存在对实体的引用,将会弹出一个对话框显示这些引用。 点击对话框上的 View Usages 按钮, 可在 Find 工具窗口中查看这些引用,这时可以根据情况点击 CancelDo Refactor 按钮。

5.2.2. 使用视图

要为实体创建新的 视图,请在项目树中选择实体,然后在右键菜单中点击 New > View

create view

视图设计界面会被打开。它包含以下字段:

  • Entity name - 要创建视图的实体的名称。

  • Name - 新视图的名称。

  • Extends - 内置或自定义视图,新视图会扩展其属性。任何实体都有三种内置视图:

  • _local 包含实体的所有本地属性(不引用其它实体的属性),

  • _minimal 包含名称模式中列出的属性,

  • _base 包括所有本地非系统属性和由 @NamePattern 定义的属性(实际上是 _minimal +_local)。

  • Configuration file - 用于存储此视图的 视图配置文件。默认情况下,Studio 在 global 模块中生成一个 views.xml 文件。

当前实体的完整属性列表显示在字段下方的树中。可以通过选中属性前面的复选框来选择要包含在视图中的属性。

如果视图继承另一个视图,则会选中所有继承的属性,并禁用相对应的复选框。

如果选择引用属性,则右侧面板中会显示以下属性:

  • Entity - 引用的实体名称。

  • View - 加载引用实体的可选视图。建议使用已命名视图而不是临时指定视图属性,因为这样可以更容易地维护复杂视图。此外,即使指定了视图名称,仍然可以通过选择属性树中的复选框来添加视图中未包含的属性。

  • Fetch - 用于引用属性的可选设置,指定如何从数据库中获取相关实体。有关详细信息,请参阅 文档

单击 OK 关闭设计界面后,可以在实体下的项目树中找到新视图:

create view 2

如果双击项目树中的视图项,Studio 将在代码编辑界面中打开 views.xml 并将光标定位在视图定义上。代码编辑界面底部有两个选项卡:TextStructure。后者显示此配置文件中定义的视图列表。

在 XML 中编辑视图定义时,使用 Ctrl+Space 自动完成属性名称:

view edit 1

要留意高亮突出显示的属性,它们很有可能不存在:

view edit 2

可以通过多种方式打开视图的图形设计界面:

  • 在项目树中选择视图,然后单击 Edit View 右键菜单项。

  • 将光标定位在配置文件代码中的视图元素上,按 Alt+Enter(Option+Enter),在弹出菜单中选择 Edit view,然后按 Enter 键。

  • 切换到配置文件代码编辑界面的 Structure 选项卡,选择视图并点击 Edit 按钮。

5.2.3. 数据库迁移

Studio 能够创建 DDL 脚本,以使数据库架构与项目的数据模型保持同步。生成的脚本可以从 Studio 直接执行,也可以由 Gradle 任务 执行,还可以在应用程序本身 启动时 执行。

要生成 DDL 脚本,请在主菜单中单击 CUBA > Generate Database Scripts,或在项目树中选择 Data Model,然后在右键菜单中单击 Generate Database Scripts

Studio 将打开 Database Scripts 窗口,窗口上有以下选项卡:

  • Updates

Updates 选项卡上显示出用来将数据库更新为数据模型当前状态的脚本。更新脚本保存在 modules/core/db/update 目录。这些脚本具有自动生成的名称,通过前缀定义执行顺序。包含 DROP 语句的脚本以红色突出显示。

可以通过单击 Create 按钮添加任意脚本,添加的脚本后续将与自动生成的脚本一起保存并执行。

单击 Remove 按钮可以编辑或完全删除新生成的脚本。

如果单击 Exclude selected(排除),这时会两个选项:

  • 将脚本移动到手动执行的脚本目录: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 constraintsInit 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} tablesInit {component} constraints 选项卡中显示。脚本分别保存在 01.{component}-create-db.sql02.{component}-create-db.sql 文件中。

单击 Save and close 以保存所有生成的脚本。可以在 Project > Data Stores > Main Data Store 项目树部分中找到脚本。

要运行更新脚本,先停止应用程序服务器(如果在运行),然后从 CUBA 主菜单执行 Update Database。如果要使用初始化脚本从头开始重新创建数据库,请执行 Create Database

5.2.4. 生成数据模型

Studio 允许为现有数据库创建数据模型和标准 UI 界面。单击 CUBA > Advanced > Generate Model 主菜单项或在项目树中选择 Project > Data Stores,然后在右键菜单中点击 Generate Model。如果有多个数据存储,Studio 会显示一个对话框,可以选择其一。

然后 Studio 打开 Generate Model from Database 向导。

  • 步骤 1

这是模型生成向导的第一步。

generate model step1

可选步骤:单击 Settings 以设置创建的新实体的 Java 包位置以及实体的系统属性与数据库列的默认映射。

例如,如果数据库中的所有或大多数表包含 ModifiedModifiedBy 列,则可以将它们映射到被创建的实体的 Updatable.updateTsUpdatable.updatedBy 属性。在这种情况下,无需为每个表单独映射它们。

使用 Exclude columns from mapping 列表可以为所有表设置不需要映射到属性的列。

向导中会列出在项目数据模型中没有对应实体的表。可以使用上面的过滤器字段按名称查找表。

选择要映射到数据模型的表。某些表通过外键依赖于其它表,因此当选择这些表时,它所依赖的所有表也将被选中。如果取消选择一个表,则也会取消选择所有依赖它的表。

可以通过单击右侧的复选框来选择或取消选择所有可用表。

单击 Next

  • 步骤 2

在此步骤中,可以查看和编辑为所选表的自动生成的映射。

generate model step2

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 按钮并选择主键的列。

Refresh mapping 按钮允许重新运行所选表的自动映射。例如,可以切换到数据库 SQL 工具,对数据库架构进行一些更改,然后返回到向导并再次执行映射过程。

Edit mapping 按钮打开一个包含映射详细信息的对话框窗口。在这里,可以更改实体名称和实体类要实现的系统接口。根据实现接口的不同,为了兼容 CUBA 的实体,会影响数据库列的创建数量。

generate model step2 2

当选中的是数据库视图并且需要选择用于实体标识符的列时,将显示 Choose PK 按钮而不是 Edit mapping

通过单击 Back,可以返回到上一步以选择或取消选择表。

单击 Next 转到下一步。

  • 步骤 3

在此步骤中,可以指定应为新实体创建哪些 UI 界面。

generate model step3

如果取消选中 Create standard screen 复选框,Studio 将不会为新实体生成 UI。

使用 In modulePackageMenu 字段指定界面源代码的位置以及在主菜单中它们的显示位置。

使用 Standard screens 列中的下拉列表选择要生成的界面类型。

可以安全地跳过此步骤,并在完成模型生成过程后为实体生成 UI 界面。

单击 Next 转到下一步。

  • 步骤 4

这是模型生成向导的最后一步。

Import scripts 表包含将在数据库上执行的脚本列表,查看这些脚本,以确认其符合要创建的实体。

在此之前,项目中不会创建任何内容,甚至也不会保存到磁盘中。Studio 实际上只会在点击 Run all scriptsRun script 时生成实体和界面并保存脚本。

可以在此页面上查看和编辑脚本,然后运行它们,或者仅保存脚本,稍后通过数据库管理工具来运行。导入脚本保存在 modules/core/db/import 目录中。

5.2.5. 集成自定义数据库

文档 中所述,框架允许使用 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 正确生成 initupdate 数据库脚本。如果不需要为此数据库生成 DDL 脚本,可跳过此步骤。

如果将自定义数据库用作主数据存储,则在生成数据库脚本时,Studio 将为所有应用程序组件(包括 CUBA)创建 init 脚本。这些脚本不包含一些必须的初始化数据,因此必须将以下内容添加到项目的 Init data 脚本中(30.create-db.sql):

  1. insert into SEC_GROUP (ID, CREATE_TS, VERSION, NAME, PARENT_ID)
  2. values ('0fa2b1a5-1d68-4d69-9fbd-dff348347f93', current_timestamp, 0, 'Company', null)^
  3. insert into SEC_USER (ID, CREATE_TS, VERSION, LOGIN, LOGIN_LC, PASSWORD, NAME, GROUP_ID, ACTIVE)
  4. values ('60885987-1b61-4247-94c7-dff348347f93', current_timestamp, 0, 'admin', 'admin',
  5. 'cc2229d1b8a052423d9e1c9ef0113b850086586a',
  6. 'Administrator', '0fa2b1a5-1d68-4d69-9fbd-dff348347f93', 1)^
  7. insert into SEC_USER (ID, CREATE_TS, VERSION, LOGIN, LOGIN_LC, PASSWORD, NAME, GROUP_ID, ACTIVE)
  8. values ('a405db59-e674-4f63-8afe-269dda788fe8', current_timestamp, 0, 'anonymous', 'anonymous', null,
  9. 'Anonymous', '0fa2b1a5-1d68-4d69-9fbd-dff348347f93', 1)^