本篇文章为你整理了实体 :: Jmix 文档(实体文件格式)的详细内容,包含有实体文档是什么 实体文件格式 实体文件夹怎么打开 实体 :: Jmix 文档,希望能帮助你了解 实体 :: Jmix 文档。
JPA 实体是按照 JPA 规则标注的 Java 类。JPA 实体将持久化到关系型数据库,数据库通过数据存储连接,可以是主数据存储,也可以是附加数据存储。
@javax.persistence.Column 注解,指定对应的数据库表字段。nullable = false 表明 数据库变更 机制应该为该字段创建具有 NOT NULL 约束的字段。
@javax.persistence.Version 注解,表示必须基于该属性进行乐观锁操作。类型必须为 Integer。如果你在 Studio 中为实体选择了 Versioned 特性,Studio 会自动创建这样一个的属性。
javax.validation.constraints 包中的 @NotNull 和 @Email 注解,是使用 Bean 验证 注解的示例。
表名和实体名都可以通过前缀避免与其他模块中的实体名称冲突。如果在 build.gradle 中设置了 jmix.projectId 属性,Studio 会用它做前缀。
特性是一组属性,赋予实体一些特定的系统级行为。这些属性由框架处理,不能由用户或你的应用程序代码修改。
Studio 的实体设计器能帮助你将可用的特性指定给实体。你也可以手动实现,创建相应的属性并按照下面的方法进行标注:
Has UUID 特性提供在内存中创建实例时自动分配一个全局唯一标识符的功能。用于使用 @JmixGeneratedValue 注解的 UUID 类型的属性上。
在 Studio 中创建实体时,如果你为字段的 Id type 选择了 UUID,则会自动使用 Has UUID 特性。如果你选择了其他类型并且 Id value 与 Generated by Jmix 不同,则需要手动添加 Has UUID 特性。
如果你的实体在内存中创建时不能马上分配一个标识属性,则强烈建议使用 Has UUID 特性。不论是映射到标识列的 Long 或 Integer 标识符,还是用户指定的任何类型,都建议这样做。如果这样的实体没有 @JmixGeneratedValue 属性,则其 hashCode() 方法总是返回一个常量值,这会影响基于哈希表的集合的性能。
Versioned(版本)特性提供了 JPA 级别的乐观锁。由一个用 @Version 注解的 integer 属性实现。
Audit of creation 和 Audit of modify 特性用来追踪创建和修改实体实例的用户和时间。由 Spring Data 的 @CreatedBy、@CreatedDate、@LastModifiedBy、@LastModifiedDate 标注的合适类型的属性实现。
Soft Delete 特性提供了实体实例的软删除功能。由 @DeletedDate 和 @DeletedBy 标注的一对属性实现,例如:
Jmix 框架使用 JPA 的继承原则。Studio 实体设计器会根据所选的继承策略自动生成必要的注解。下面是一些主要的注解:
在选择 SINGLE_TABLE 和 JOINED 继承策略时,用该注解定义一个数据库列,此列负责区分实体的类型。
@DiscriminatorColumn(name = "TYPE", discriminatorType = DiscriminatorType.INTEGER)
定义该类为某些实体的祖先,其属性必须在后代实体中使用。这种类型的类不关联任何特定的数据库表。
应用程序的数据模型可能包含仅存在于内存中的实体,或者使用非 JPA 机制映射的一些外部数据。我们称此类实体为 DTO,因为它们通常被用作参数中的数据传输对象,也会在 REST API 中或与外部 API 通信时被用做返回值。
DTO 实体可以与 自定义数据存储 关联,通过 DataManager 进行通用的 CRUD 操作,并能自动解析 JPA 实体对 DTO 实体的引用。
在下面的示例中,你还可以看到如何将某些对象属性排除在实体属性之外(在 实体属性 章节有更多介绍):
@Store(name = "inmem") (1)
@JmixEntity(name = "sample_Metric", annotatedPropertiesOnly = true) (2)
public class Metric {
@JmixProperty(mandatory = true) (3)
@JmixId (4)
@JmixGeneratedValue (5)
private UUID id;
@JmixProperty (6)
private String name;
@JmixProperty (6)
private Double value;
private Object ephemeral; (7)
public UUID getId() {
return id;
public void setId(UUID id) {
this.id = id;
// other getters and setters
@JmixEntity 注解的 annotatedPropertiesOnly = true 参数表示未使用 @JmixProperty 标注的对象属性不是实体属性。
由于 @JmixEntity 注解设置了 annotatedPropertiesOnly = true 参数,没有注解的属性则不是实体属性。
KeyValueEntity 允许你将任意数量的命名值集合作为实体,用来处理不能使用 Java 类(JPA 或 DTO 实体)表示的数据。
比如这个例子:你的数据模型中有 Order(订单)实体,你需要计算按客户汇总的订单金额总和并在 UI 展示。可以执行一个 JPQL 查询并使用 DataManager 将结果集加载为一个 KeyValueEntity 实例列表:
List KeyValueEntity entities = dataManager.loadValues(
"select e.customer, sum(e.amount) from sample_Order e group by e.customer")
.properties("customer", "total")
.list();
返回的 KeyValueEntity 实例具有你在 properties() 方法中指定的两个属性:customer 属性值为查询结果集中第一个字段的值,而 total 为第二个字段的值。可以按如下方式读取:
for (KeyValueEntity entity : entities) {
Customer customer = entity.getValue("customer");
BigDecimal totalAmount = entity.getValue("total");
// ...
}
实体属性的英文术语除了 Entity Attributes,还有另一个术语:Entity Properties。经常在 Jmix 代码中使用,比如这些注解:@JmixProperty、@DependsOnProperties 等。
如果你需要的类型不在上述列表,你可以实现一个对应的 数据类型 并确保 数据存储 支持该类型,然后就可以作为实体属性的类型使用。
Field-based attribute,基于字段的属性,对应一个字段和该字段的一对存取方法(getter / setter)。字段名称作为属性名称。
setter 可以省略,表示该属性为只读。
Method-based attribute,基于方法的属性,对应一个不带参数的方法,它返回支持的类型,并且名称以 get 开头,比如 getCustomer()。方法名去掉 get,剩下的部分首字母改为小写则为属性名:getFullName() fullName。
基于方法的属性示例:
实体类的某些属性(字段 + getter/setter)和非实体属性的类方法,可以不包含在实体元数据中。因此,虽然可以在应用程序代码中使用它们,但框架无法识别这些字段和方法,也不会在 UI 中显示或通过 REST API 传输。
如果 @JmixEntity 注解的 annotatedPropertiesOnly 参数为 false(默认值),则以下对象属性为实体属性:
如果 annotatedPropertiesOnly 参数设置为 true,则只有用 @JmixProperty 标注的属性和方法为实体属性。
默认情况下,关系是一种 association,这意味着两个实体可以不依赖彼此独立存在,互相没有所有权。例如,在一个 Customer - Order(客户-订单)关系中,Order 有一个属性是对 Customer 的引用。用户分别创建 Customer 和 Order,为 order 选择 customer,甚至在需要的时候改为对另一个 customer 的引用。
Jmix 还支持一种更强的实体关联关系,叫做 composition。composition(组合)意味着存在所有权,一个实体实例只能作为其所有者实体实例的一部分存在。例如,在 Order - OrderLine 关系中,Order 有一个属性,是 OrderLine 实例的集合。每个 OrderLine 实例都是为特定的 Order 创建,是 Order 的一部分,不能属于另一个 Order。
组合 composition 关系的实体在 UI 中一起编辑。例如,用户打开一个 Order 编辑界面,可以在其单独的编辑界面中创建和编辑 OrderLines,但是 Order 及其所有的 OrderLines 更改会在同一个事务中一起保存到数据库中,并且是只在用户确认保存所有者实体 - Order 的时候。
当从不同的数据存储中选择实体做关联时,Studio 实体设计器会自动定义跨数据存储引用的属性集。
例如,在主数据存储中有 Customer 实体,在附加数据存储中有 Address 实体,并且你希望有从 Customer 到 Address 的引用。那么 Customer 实体应该包含以下两个属性:
addressId 属性存储 Address 的标识符。带 @SystemLevel 注解,提示框架该属性不应显示给用户。
address 属性包含对 Address 实体的引用。这个属性是 @Transient(不会存储在数据库),使用 @DependsOnProperties 注解,提示框架该属性值依赖于另一个属性。
之后,当你使用包含 address 属性的 fetch plan 加载 Customer 时,DataManager 会自动从附加数据存储中加载相关的 Address。本框架优化了集合的加载性能:加载客户列表后,会从附加数据存储中批量加载引用。批量的数目由 jmix.core.cross-data-store-reference-loading-batch-size 应用程序属性定义。
当保存带 Address 的 Customer 实体图时,DataManager 通过相应的 DataStore 保存实例,然后将地址的标识符保存在 customer 的 addressId 属性中。
创建 JPA 和 DTO 实例时,请使用适当的框架接口,不要使用 new 运算符调用类构造函数;这样可以保证框架正确的初始化 @JmixGeneratedValue 标注的字段并调用 @PostConstruct 方法。
如果你在编写业务逻辑并且在代码中已经有 DataManager,可以使用其 create() 方法,这样可以避免注入 Metadata bean。例如:
Order createAndSaveOrder(String number) {
Order order = dataManager.create(Order.class);
order.setNum(number);
dataManager.save(order);
return order;
}
在 UI 界面中,上述两种方法都可以使用。但是你可能还希望将创建的实例自动保存在界面的 DataContext 提交中,此时,请使用 DataContext.create() 方法,会创建实例并立即 merge,开始跟踪其变化。在下面的示例中,我们创建了一个 ProductPart 实体实例,merge 到 DataContext 中,并添加到一个数据容器以显示在 UI 表格中:
@Subscribe("partsTable.create")
public void onPartsTableCreate(Action.ActionPerformedEvent event) {
ProductPart part = dataContext.create(ProductPart.class);
partsDc.getMutableItems().add(part);
}
Jmix 依赖数据库的唯一约束来管理实体实例的唯一性。因此,如果需要设置实体的某个属性或者一组属性唯一,则需要为数据库表创建相应的索引。
Studio 实体设计器 包含 Indexes 标签页,可以定义唯一索引。索引定义保存在实体的 @Table 注解中,后续 Liquibase 用来创建数据库结构中的索引。
@DbView 注解表示一个 JPA entity 实体是否映射到数据库视图。这类实体不会生成数据库变更脚本。
unmappedColumns - 存在于数据库中但不应映射到实体的列。不会生成这些列的删除脚本;
unmappedConstraints - 存在于数据库中但不应映射到实体的约束和索引。不会生成这些列的删除脚本。
@DependsOnProperties 注解指定该属性所依赖的实体属性。在构建 fetch plans 以及加载/保存来自不同数据存储的实体的引用时,会考虑这些属性。此外,如果被标注的属性是只读的(没有 setter 方法),则在更改它时会发送 EntityPropertyChangeEvent 事件。
Instance name 是表示一个实体实例的可阅读文本。可以将它理解为应用程序级别的 toString() 方法。广泛用于在界面的单个控件或表格控件的单元格中显示实体实例。还可以用编程的方式通过 MetadataTools.getInstanceName() 方法获取实例名称。
如果想生成比单个属性值更复杂的结果,可以在实体类中创建一个返回类型为 String 的方法。例如:
@DependsOnProperties({"latitude", "longitude"})
public String getDisplayName(Messages messages) {
return messages.formatMessage(
getClass(), "GeoPointEntity.instanceName", this.latitude, this.longitude);
}
该方法可以接受任何 Spring beans 作为参数。在上面的示例中,Messages bean 用来根据当前用户的区域设置格式化实例名称。
实例名称方法的 @DependsOnProperties 注解是必要的,因为它指定了内置的 _instance_name fetch plan 需要的属性。
如果类被 @javax.persistence.Entity 标注,框架会从中获取元数据的实体名称,这时 @JmixEntity 不能指定 name 参数。如果没有被 @javax.persistence.Entity 标注,则在 @JmixEntity 的 name 参数中指定实体名称。如果 @JmixEntity 和 @javax.persistence.Entity 都没有 name 参数,则实体名称等于其 Java 类的简单名称。
annotatedPropertiesOnly 参数指定哪些对象属性会成为实体属性,请参阅 实体属性 了解更多详细信息。
@JmixGeneratedValue 注解表示在内存中创建实体实例时必须由框架生成并分配实体属性值。
被标注的属性必须是 Long、Integer 或 UUID 类型,并且一个实体不能有多个带此注解的 UUID 属性。
请注意,如果你使用 new 运算符创建实体实例,@JmixGeneratedValue 注解不会生效。有关创建新实例的正确方法,请参阅 实例化实体。
@JmixId 注解为 DTO 实体 指定实体标识。如果你的 DTO 实体映射到一些外部数据并且你需要重复加载/保存其实例,则应该显式地选择一个标识符,因为在这种情况下,你需要在实体生命周期中维护对象标识。
如果没有这样的自然存在的唯一属性值,可以创建一个并用 @JmixGeneratedValue 注解在创建实例时分配唯一值:
@JmixProperty 注解表示该属性或方法是实体属性。更多详细信息,请参阅 实体属性。
使用 mandatory 参数来表示该属性需要有值,如果对象字段没有 JPA @Column 注解,你可以设置 nullable = false。
被标注的方法接受任何 Spring bean。在下面的示例中,我们使用 TimeSource bean 来初始化一个 date 属性:
如果你使用 new 运算符创建实体实例,@PostConstruct 注解的方法不会被调用到。有关正确的创建新实例的方法,请参阅 实例化实体。
如果实体属性的 Java 类型有多个 数据类型,则 @PropertyDatatype 注解可以通过其 id 显式指定 Datatype 实现。例如:
以上就是实体 :: Jmix 文档(实体文件格式)的详细内容,想要了解更多 实体 :: Jmix 文档的内容,请持续关注盛行IT软件开发工作室。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。