5.2. 数据模型

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

5.2.1. 使用实体

创建新实体

  1. 在项目树中选择 Data Model 部分或它下面的包,然后从右键菜单中选择 New > Entity

  2. 出现 New CUBA Entity 对话框。在 Entity name 字段中输入实体类的名称,选择实体及其 ID 的类型。

  3. Studio 将创建实体类,根据实体类型的不同在 persistence.xmlmetadata.xml 中进行注册。创建的类会在源代码编辑界面中打开。

Studio 在实体源码编辑器底部显示 4 个标签页。一起组成实体可视化设计器:

  • Text 包含源码。

  • Designer 显示实体设计界面,可以使用图形界面配置实体及其属性,而不用编写 Java 代码。

    • Indexes 展示索引,并且可以为选中的实体创建新索引。
  • DDL Preview 包含相应表及其参考约束的只读 DDL 代码。

entity designer

Tip

实体设计器只有激活 CUBA Studio 订阅才能使用。

创建实体属性

有多种方法可以向实体添加属性。

  • 使用实体设计器的图形化界面:切换到 Designer 选项卡,单击 Attributes 表下方的 New 并填写 New Attribute 窗口中的必填字段。

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

    attribute l10n

  • 使用从源码打开的独立窗口。在源码中将光标定位在最后一个字段下方,然后按下 Alt+Insert (Cmd+N)。在 Generate 菜单中,选择 Add Attribute。Studio 将显示 New Attribute 窗口,和从图形界面打开的一样。在代码编辑器的操作面板,也可以点击 Add attribute 按钮打开同样的窗口。

    new attribute 2

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

Studio 可以帮助将新增的属性添加到包含该实体的 UI 界面。打开 Text 标签页,然后点击源码编辑器顶部的 Add attributes to screens 操作。Studio 会搜索所有使用当前实体的界面,并分析哪些属性可以添加至这些界面的组件内。然后可以在对话框中选取需要的属性和界面组件:

add attrs to screens

还有一个类似功能的 Add to Screens 按钮,在实体的 Designer 标签页中 Attributes 表格的上方。

创建实例名称

可以用作另一个实体的引用属性的实体(例如 Customer 可以是 Order 的属性)需要一个 pattern 来生成实例的有意义的名称。此 pattern 由实体类上的 @NamePattern 注解定义。

如果您使用实体设计器编辑实体,可以有两种方式设置实体实例名称:

  • 使用可视化构建器。可以点击 Instance name 字段的铅笔按钮:

    entity designer instance name

  • 自动模式。实体设计器检测到为实体添加下列属性之一时会自动生成实例名称:nametitlecaptionlabelsummarydescriptionfirstNamelastNamemiddleName

实例名称还可以从实体的源代码创建,将光标定位在类名称上,按 Alt+EnterOption+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

DDL 生成设置

Designer 标签页还可以配置 DDL 生成设置,能修改 Studio 为该实体生成数据库迁移脚本的方式。

移除实体

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

remove entity

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

5.2.2. 使用枚举

Studio 提供一组操作和可视化设计器帮助在应用程序中使用 枚举

枚举与实体一并在 CUBA 项目树的 Data Model 部分展示。

创建新的枚举

  1. 在项目树中选择 Data Model 或者其下面的一个包名,右键点击后选择 New > Enumeration

  2. 弹出 New CUBA Enumeration 对话框。在 Class 字段输入枚举类的名称,选择包名,Id 类型(推荐 String)。

create enum

  1. Studio 将会创建枚举类,创建的类会在源码编辑器打开。编辑器的底部会显示两个标签页:

    • Text 包含源代码。

    • Designer 显示枚举设计器,这里可以使用图形界面配置枚举和值(常量),避免写 Java 代码。

enum designer

使用 Values 表格和关联的按钮设置枚举值常量。

  • Name 列用于输入代码中使用的常量名称。这个名称可以后期修改而不影响数据库已经存在的数据。

  • Value 列用于输入枚举常量的 id。这是数据库保存的实际值。

  • 地球按钮用于为选中的常量设置本地化名称(用户友好名称)。

设计器也提供将枚举的 Id type 从 String 修改为 Integer(或反过来)的功能。只需要打开枚举设计界面然后切换 Id type 的值。Studio 会自动在代码中对使用该枚举的地方做代码迁移。之后,您可以修改已有的枚举常量。注意,这种改动不会对数据库表格已存在的枚举值做改动,需要手动做数据迁移,比如可以通过一个数据库的更新脚本来实现。

5.2.3. 使用视图

视图 是对象关系图的一种描述,当实体从数据库加载时,用视图定义需要从数据库加载哪些属性、集合以及子实体。视图可以在界面描述中定义,也可以在单独的 views.xml 共享文件中定义,在后者定义的视图全局可用。

要为实体创建新的共享视图,请在项目树中选择实体,然后在右键菜单中点击 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 并将光标定位在点击的视图定义处。代码编辑界面底部有两个选项卡:TextStructureStructure 标签显示此配置文件中定义的视图列表,并能可视化构建视图定义:

view designer

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

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

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

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

使用视图设计器可以安全的重命名视图,能自动修改所有用到修改之前视图名称的地方。如需重命名视图,点击 Name 字段右侧的小铅笔按钮编辑即可。

Tip

视图设计器需要激活 CUBA Studio 订阅才能使用。

手动编辑视图

也可以手动修改 views.xml 的源码来编辑视图定义。Studio 对视图配置文件的 XML 结构提供了完善的代码辅助支持。

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

view edit 1

需要注意高亮的属性,很可能他们不存在:

view edit 2

5.2.4. 数据库迁移

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

可以为下列情况创建 DDL 脚本:

  • 主数据存储

  • 附加数据存储,需要使用 Create and UpdateUpdate Only schema 管理模式。

有下列操作可以生成 DDL 脚本:

  • 从主菜单选择 CUBA > Generate Database Scripts - 这里是为所有的数据存储生成脚本。

  • 在项目树选择 Data Model 然后点击右键菜单的 Generate Database Scripts - 这里是为所有的数据存储生成脚本。

  • 在 CUBA 项目树选择特定数据存储的节点,然后右键点击,菜单中选择 Generate Database Scripts - 这里只为选中的数据存储生成脚本。

然后 Studio 会打开 Database Scripts 窗口:

database scripts dialog

左侧的面板按数据存储对脚本做了分组。只有支持 DDL 脚本的数据存储才会显示。

数据库脚本有以下分类:

Updates

Updates 显示用来将数据库更新为数据模型当前状态的脚本。右侧的表格展示脚本列表,底部展示脚本内容。如果您的项目包含未执行的更新脚本,则会在左侧树高亮显示对应节点。更新脚本保存在 modules/core/db/updatemodules/core/db/update_{additional_data_store_name} 目录。这些脚本具有自动生成的名称,通过前缀定义执行顺序。包含 DROP 语句的脚本以红色突出显示。

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

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

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

  • 将脚本移动到手动执行的脚本目录:modules/core/db/update-manuallymodules/core/db/update-manually_{additional_data_store_name}。然后当运行 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 DatabaseUpdate All Databases。如果只想更新一个数据存储,可以在 CUBA 项目树选中该存储节点,然后在右键菜单中选择 Update Database

如果要使用初始化脚本从头开始重新创建数据库,请执行 Create DatabaseCreate All Databases。如果只想重新创建一个数据存储,可以在 CUBA 项目树选中该存储节点,然后在右键菜单中选择 Create Database

实体 DDL 脚本生成配置

可以在实体级别自定义 Studio 数据库迁移脚本的生成行为。实体设计器Designer 标签页能配置当前实体的 DDL generation settings

entity ddl gen settings

  • DB script generation mode - 选择合适的模式:

    • CREATE_AND_DROP - 默认模式。当生成 DDL 脚本时,Studio 可以在数据库创建表、列以及约束,并且可以在列或约束对应的数据模型属性不存在时 drop 掉列或约束。可以根据数据模型创建 initupdate 脚本。

    • CREATE_ONLY - 在生成 update 脚本时,Studio 不会尝试 drop 列或约束。可以根据数据模型创建 Init 脚本。

    • DISABLED - Studio 不为该实体创建任何 initupdate 脚本。

  • Unmapped columns - 一组只在数据库存在的列而没有对应的实体属性。Studio 不会尝试 drop 掉这些列。

  • Unmapped constraints - 一组只在数据库存在的约束而没有对应的实体属性。Studio 不会尝试在 update 脚本中 drop 掉这些约束。

这些设置也可以在源码手动配置,参阅 com.haulmont.cuba.core.global.DdlGeneration 注解(最低要求平台版本:7.1.6 或 7.2.2)。

5.2.5. 生成数据模型

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.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 正确生成 initupdate 数据库脚本。如果不需要为此数据库生成 DDL 脚本,可跳过此步骤。

为主数据存储初始化数据脚本

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

  1. insert into SEC_GROUP (ID, CREATE_TS, VERSION, NAME, PARENT_ID)
  2. values ('0fa2b1a5-1d68-4d69-9fbd-dff348347f93', now(), 0, 'Company', null)^
  3. insert into SEC_USER (ID, CREATE_TS, VERSION, LOGIN, LOGIN_LC, PASSWORD, PASSWORD_ENCRYPTION, NAME, GROUP_ID, ACTIVE)
  4. values ('60885987-1b61-4247-94c7-dff348347f93', now(), 0, 'admin', 'admin',
  5. '$2a$10$vQx8b8B7jzZ0rQmtuK4YDOKp7nkmUCFjPx6DMT.voPtetNHFOsaOu', 'bcrypt',
  6. 'Administrator', '0fa2b1a5-1d68-4d69-9fbd-dff348347f93', true)^
  7. insert into SEC_USER (ID, CREATE_TS, VERSION, LOGIN, LOGIN_LC, PASSWORD, NAME, GROUP_ID, ACTIVE)
  8. values ('a405db59-e674-4f63-8afe-269dda788fe8', now(), 0, 'anonymous', 'anonymous', null,
  9. 'Anonymous', '0fa2b1a5-1d68-4d69-9fbd-dff348347f93', true)^
  10. insert into SEC_USER_ROLE (ID, CREATE_TS, VERSION, USER_ID, ROLE_NAME)
  11. values ('6736effb-9dfc-4430-973a-69868606b09c', current_timestamp, 0, '60885987-1b61-4247-94c7-dff348347f93', 'system-full-access')^

添加 JDBC 驱动

当生成数据库迁移脚本时,Studio 需要对应数据库的 JDBC 驱动。使用自定义数据库,需要手动下载驱动文件并放置于 {USER_HOME}/.haulmont/studio/lib 目录(比如 windows 上:C:\Users\{username}\.haulmont\studio\lib )。

在文件夹添加完驱动之后,别忘做下面几步:

  • 重启 IDE。

  • 停止 Gradle daemon(在终端执行 gradlew --stop)。

集成 Firebird 数据库示例

集成自定义数据库(Firebird)至 CUBA 和 Studio 的示例可以在这里找到: https://github.com/cuba-labs/firebird-sample 。这个例子也可以作为扩展插件添加至您的项目以支持 Firebird 数据库。