3.4.2. 数据存储

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

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

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

在任何 CUBA 应用程序中,必定存在一个主数据存储,它包含系统实体和安全实体,用户登录也是在主数据存储。在本手册中提及数据库时,如果没有明确说明,则指的是主数据存储。主数据存储必须是通过 JDBC 数据源连接的关系型数据库。附加数据存储可以是任何 DataStore 接口的实现。

使用 CUBA Studio 可以配置附加数据存储,参考 文档 。会自动创建所有需要的应用程序属性和 JDBC 数据源,并能维护额外添加的 persistence.xml 文件。设置好数据存储之后,在实体设计器界面的 Data store 字段可以选择其为实体的存储位置。在使用 Generate Model 向导为已有数据库 schema 创建映射实体时,也可以选择不同的数据存储。

如果没有使用 Studio 的话,下面的信息可以帮助你排查问题。

附加数据存储的名称通过 cuba.additionalStores 应用程序属性指定。如果附加存储是 RdbmsStore,还为其定义了下面这些属性:

  • cuba.dbmsType_<store_name> - 数据存储 DBMS 类型。

  • cuba.persistenceConfig_<store_name> - 数据存储对应的 persistence.xml 文件位置。

  • cuba.dataSource…​ - 连接至数据库描述的连接参数。

如果项目中实现了 DataStore 接口,实现类的名称必须使用 cuba.storeImpl_<store_name> 属性定义。

比如,如果使用了两个附加存储 db1(PostgreSQL 数据库)和 mem1(一个自定义的内存存储,通过某些项目中的 bean 实现),core 模块的 app.properties 文件必须定义以下属性:

cuba.additionalStores = db1, mem1

# RdbmsStore for Postgres database with data source obtained from JNDI

cuba.dbmsType_db1 = postgres
cuba.persistenceConfig_db1 = com/company/sample/db1-persistence.xml
cuba.dataSourceJndiName_db1 = jdbc/db1

# Custom store
cuba.storeImpl_mem1 = sample_InMemoryStore

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

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

如果正确定义了 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 会自动维护这种引用关系。