3.4.2. 数据存储

在 CUBA 应用程序中处理数据的常用方法是操作实体 - 可通过数据源和具有数据感知功能的可视化组件进行声明式处理,也可通过 DataManagerEntityManager 进行编程式处理。实体映射到数据存储中的数据,数据存储通常是关系型数据库。应用程序可以连接到多个数据存储,因此其数据模型将包含映射到位于不同数据库中的数据的实体。

实体只能属于单个数据存储,但是可以在单个 UI 界面上显示来自不同数据存储的实体,DataManager 可以确保在保存时将实体分派到适当的数据存储中去。根据实体类型,DataManager 选择一个已注册的数据存储,这个数据存储实现了 DataStore 接口,然后委托其加载和保存实体。当以编程的方式控制事务并通过 EntityManager 使用实体时,必须明确指定要使用的数据存储。有关详细信息,请参阅 Persistence 接口方法和 @Transactional 注解参数。

平台提供了 DataStore 接口的单一实现,名称为 RdbmsStore,这个实现的目的是通过 ORM 层来使用关系型数据库。可以在自己的项目中实现 DataStore 接口以进行数据整合,例如,可以与非关系型数据库或具有 REST 接口的外部系统进行数据整合。

在任何 CUBA 应用程序中,必定存在一个主数据存储,它包含系统实体和安全实体,用户登录也是在主数据存储。在本手册中提及数据库时,如果没有明确说明,则指的是主数据存储。主数据存储必须是通过 JDBC 数据源连接的关系型数据库。主数据源位于 JNDI 中,其名称应在 cuba.dataSourceJndiName 应用程序属性中指定,默认情况下为 jdbc/CubaDS

可以在 cuba.additionalStores 应用程序属性中指定其它附加数据存储名称。如果附加存储是 RdbmsStore,需要提供以下属性:

  • cuba.dataSourceJndiName_{store_name} - 相应 JDBC 数据源的 JNDI 名称。

  • cuba.dbmsType_{store_name} - 数据存储 DBMS 的类型。

  • cuba.persistenceConfig_{store_name} - 数据存储 persistence.xml 文件的位置。

如果在项目中为附加存储实现了 DataStore 接口,需要在 cuba.storeImpl_{store_name} 应用程序属性中指定实现 bean 的名称。

例如,如果需要使用另外两个数据存储:db1(PostgreSQL 数据库)和 mem1(由某个项目 bean 实现的内存存储),请在 core 模块的 app.properties 中指定以下应用程序属性:

cuba.additionalStores = db1, mem1
cuba.dataSourceJndiName_db1 = jdbc/db1
cuba.dbmsType_db1 = postgres
cuba.persistenceConfig_db1 = com/company/sample/db1-persistence.xml
cuba.storeImpl_mem1 = sample_InMemoryStore

还应在所有应用程序 block 使用的属性文件(web-app.propertiesportal-app.properties 等)中指定 cuba.additionalStorescuba.persistenceConfig_db1 属性。

CUBA Studio 允许在 CUBA Project Properties 窗口的 Data Stores 标签页上设置其它数据存储。它会自动创建所有必须的应用程序属性和 JDBC 数据源,同时维护额外的 persistence.xml 文件。之后,可以在实体设计器的 Data store 字段中为实体选择数据存储。当使用 Generate model 向导为已有数据库创建实体时,也可以选择数据存储。

来自不同数据存储的实体之间的引用

如果正确定义了 DataManager,则可以自动维护来自不同数据存储的实体之间的 TO-ONE 引用。比如,在主数据存储中有 Order 实体,在附加数据存储中有 Customer 实体,并且希望在 Order 中引用 Customer 。可以这样做:

  • Order 实体中,定义一个存储 Customer 实体 ID 的属性。该属性应使用 @SystemLevel 注解,以将其从用户可用的各种列表中排除,例如 Filter 中的可选属性:

    @SystemLevel
    @Column(name = "CUSTOMER_ID")
    private Long customerId;
  • Order 实体中,定义对 Customer 的非持久引用,并将 "related" 指定为 customerId

    @Transient
    @MetaProperty(related = "customerId")
    private Customer customer;
  • 在适当的视图中包含非持久化的 customer 属性。

之后,当使用包含 customer 属性的视图加载 Order 时,DataManager 会自动从附加数据存储加载关联的 Customer。集合的加载针对性能进行了优化:在加载 Order 列表之后,从附加数据存储加载引用的 Customer 是分批完成的。每批加载的记录数由 cuba.crossDataStoreReferenceLoadingBatchSize 应用程序属性定义。

当提交引用了 CustomerOrder 实体图时,DataManager 会通过相应的 DataStore 进行数据保存,然后将 Customer 的 ID 保存在 Order 的 customerId 属性中。

Filter 组件也支持跨数据存储引用。

在 Studio 中,如果使用了跨数据存储的实体引用属性,Studio 会自动维护这种引用关系。