[Java programming language only]

定义实体模式

ObjectGrid 可以具有任意数量的逻辑实体模式。 实体使用注释的 Java™ 类、XML 或 XML 和 Java 类的组合进行定义。 然后,定义的实体会向 eXtreme Scale 服务器注册,并绑定到 BackingMap、索引和其他插件。

设计实体模式时,必须完成以下任务:
  1. 定义实体及其关系。
  2. 配置 eXtreme Scale。
  3. 注册实体。
  4. 创建与 eXtreme Scale EntityManager API 交互的基于实体的应用程序。

实体模式配置

实体模式为一组实体以及实体之间的关系。在具有多个分区的 eXtreme Scale 应用程序中,以下限制和选项适用于实体模式: 实体在初始化之前会向 ObjectGrid 实例注册。每个定义的实体必须采用唯一名称进行命名,且自动绑定到具有相同名称的 ObjectGrid BackingMap。根据您正使用的配置,初始化方法会有所不同:

本地 eXtreme Scale 配置

如果您正使用本地 ObjectGrid,那么可通过编程方式配置实体模式。在此方式中,可以使用 ObjectGrid.registerEntities 方法,以注册带注释的实体类或实体元数据描述符文件。

分布式 eXtreme Scale 配置

如果您正使用分布式 eXtreme Scale 配置,那么必须为实体元数据描述符文件提供实体模式。

有关更多详细信息,请参阅分布式环境中的实体管理器

实体需求

使用 Java 类文件、实体描述符 XML 文件或这两者来配置实体元数据。至少需要实体描述符 XML 确定哪些 eXtreme Scale BackingMap 将与实体关联。在带注释的 Java 类(实体元数据类)或实体描述符 XML 文件中,描述实体的持久属性以及此实体与其他实体的关系。实体元数据类(指定时)也由 EntityManager API 使用,以与数据网格中的数据交互。

eXtreme Scale 网格可以在不提供任何实体类的情况下进行定义。当服务器和客户机直接与底层映射中存储的元组数据交互时,这可能会有用。这些实体完全在实体描述符 XML 文件中进行定义,且称为无类实体。

无类实体

当无法在服务器或客户机类路径中包含应用程序类时,无类实体有用。这些实体在实体元数据库描述符 XML 文件中进行定义,其中,实体的类名通过使用无类实体标识进行指定,格式为:@<entity identifier>。@ 符号将实体标识为无类,且用于映射实体之间的关联。请参阅“无类实体元数据”图,此图是实体元数据描述符 XML 文件(定义了两个无类实体)的示例。

如果 eXtreme Scale 服务器或客户机无权访问这些类,那么它们仍可以利用使用无类实体的 EntityManager API。常见用例包括以下用例:

  • eXtreme Scale 容器在服务器中托管,此服务器不允许类路径中具有应用程序类。在此情况下,客户机仍可使用来自客户机(允许存在类)的 EntityManager API 访问网格。
  • eXtreme Scale 客户机不需要对实体类的访问权,这是因为客户机正使用非 Java 客户机(例如 eXtreme Scale REST 数据服务)或客户机正使用 ObjectMap API 访问网格中的元组数据)。

如果客户机和服务器之间的实体元数据兼容,那么可以使用实体元数据类、XML 文件或这两者来创建实体元数据。

例如,下图中的“程序化实体类”与下一部分中的无类元数据代码兼容。

程序化实体类
@Entity
public class Employee {
    @Id long serialNumber;
    @Basic byte[] picture;
    @Version int ver;
    @ManyToOne(fetch=FetchType.EAGER, cascade=CascadeType.PERSIST)
    Department department;
}

@Entity
public static class Department {
    @Id int number;
    @Basic String name;
    @OneToMany(fetch=FetchType.LAZY, cascade=CascadeType.ALL, mappedBy="department")
    Collection<Employee> employees;
}

无类字段、键和版本

如上所述,无类实体完全在实体 XML 描述符文件中配置。基于类的实体使用 Java 字段、属性和注释定义其属性。因此,无类实体需要使用 <basic><id> 标记在实体 XML 描述符中定义键和属性结构。

Classless entity metadata
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://ibm.com/ws/projector/config/emd"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://ibm.com/ws/projector/config/emd ./emd.xsd">

        			<attributes>
        <id name="serialNumber" type="long"/>
        <basic name="firstName" type="java.lang.String"/>
        <basic name="picture" type="[B"/>
        <version name="ver" type="int"/>
        <many-to-one 
            name="department" 
            target-entity="@Department"
            fetch="EAGER"">
                <cascade><cascade-persist/></cascade>
        </many-to-one>
    </attributes>
</entity>

        			<attributes>
        <id name="number" type="int"/>
        <basic name="name" type="java.lang.String"/>
        <version name="ver" type="int"/>
        <one-to-many 
            name="employees" 
            target-entity="@Employee" 
            fetch="LAZY" 
            mapped-by="department">
            <cascade><cascade-all/></cascade>
        </one-to-many>
    </attributes>
</entity>

请注意,以上每个实体都具有 <id> 元素。无类实体必须具有一个或多个定义的 <id> 元素,或表示实体的键的单值关联。实体的字段由 <basic> 元素表示。<id>、<version> 和 <basic> 元素需要在无类实体中具有名称和类型。请参阅以下受支持属性类型部分,以获取有关受支持类型的详细信息。

实体类需求

通过将各种元数据与 Java 类关联来标识基于类的实体。可以使用 Java Platform, Standard Edition V5 注释、实体元数据描述符文件或注释和描述符文件的组合来指定元数据。实体类必须满足以下条件: 所有实体都具有唯一名称和类型。如果使用注释,那么缺省情况下,名称为类的简单(短)名称,但可使用 @Entity 注释的 name 属性对其进行覆盖。

持久属性

客户机或实体管理器可通过使用字段(实例变量)或 Enterprise JavaBean 样式的属性访问器来访问实体的持久状态。每个实体必须定义基于字段或基于属性的访问。如果对类字段进行了注释,那么带注释的实体为基于字段访问的实体,如果对属性的 getter 方法进行注释,那么带注释的实体为基于属性访问的实体。不允许混合使用字段访问和属性访问。 如果无法自动确定类型,那么可以使用 @Entity 注释上的 accessType 属性或等效 XML 来确定访问类型。

持久字段
直接从实体管理器和客户机访问字段访问实体实例变量。将忽略使用瞬态修饰符或瞬态注释标记的字段。持久字段不得具有 finalstatic 修饰符。
持久属性
属性访问实体必须遵循读和写属性的 JavaBeans 签名约定。将忽略不遵循 JavaBeans 约定或在 getter 方法上具有 Transient 注释的方法。针对类型为 T 的属性,必须具有 getter 方法 getProperty(此方法返回类型 T 的值)和 void setter 方法 setProperty(T)。 针对布尔类型,getter 方法可表示为 isProperty,同时返回 true 或 false。持久属性不能具有静态修饰符。
受支持的属性类型
支持以下持久字段和属性类型:
  • Java 基本类型(包含包装程序)
  • java.lang.String
  • java.math.BigInteger
  • java.math.BigDecimal
  • java.util.Date
  • java.util.Calendar
  • java.sql.Date
  • java.sql.Time
  • java.sql.Timestamp
  • byte[]
  • java.lang.Byte[]
  • char[]
  • java.lang.Character[]
  • enum
用户可序列化属性类型受支持,但具有性能、查询和更改检测限制。无法通过代理访问的持久数据(例如,数组和用户可序列化对象)必须重新分配给实体(如果更改)。

使用对象的类名在实体描述符 XML 文件中表示可序列化属性。如果对象是一个数组,那么使用 Java 内部形式来表示数据类型。例如,如果属性数据类型为 java.lang.Byte[][],那么字符串表示为 [[Ljava.lang.Byte;

用户可序列化类型应遵循以下最佳实践:

  • 实现高性能序列化方法。实现 java.lang.Cloneable 接口和公共克隆方法。
  • 实现 java.io.Externalizable 接口。
  • 实现 equals 和 hashCode

实体关联

可以将双向和单向实体关联或实体之间的关系定义为一对一、多对一、一对多以及多对多。存储实体时,实体管理器自动将实体关系解析为相应键引用。

eXtreme Scale 网格为数据高速缓存,不会强制实施引用完整性(如,数据库)。虽然关系允许级联存在且为子实体移除操作,但这不会检测对象的中断链接,或强制实施中断链接。移除子对象时,必须从父级移除对该对象的引用。

如果您在两个实体之间定义双向关联,那么必须确定关系所有者。在一对多或多对多的关联中,关系的多一方始终是所有者。如果无法自动确定所有权,那么必须指定注释的 mappedBy 属性或 XML 等效项。mappedBy 属性确定为关系所有者的目标实体中的字段。当具有多个类型和基数相同的属性时,此属性还帮助确定相关字段。

单值关联

使用 @OneToOne 和 @ManyToOne 注释或等效 XML 属性表示一对一和多对一关联。 目标实体类型由属性类型确定。以下示例定义 Person 和 Address 之间的单向关联。 Customer 实体具有对一个 Address 实体的引用。在此情况下,关联也可以为多对一,这是因为没有反向关系。
@Entity
public class Customer {
  @Id id;
  @OneToOne Address homeAddress;
}

@Entity
public class Address{
  @Id id
  @Basic String city;
}
要在 Customer 和 Address 类之间指定双向关系,请从 Address 类添加对 Customer 类的引用,并添加相应注释以标记关系的反向方。由于此关联是一对一关联,因此必须在 @OneToOne 注释上使用 mappedBy 属性来指定关系的所有者。
@Entity
public class Address{
  @Id id
  @Basic String city;
  @OneToOne(mappedBy="homeAddress") Customer customer;
}

集合值关联

使用 @OneToMany@ManyToMany 注释或等效 XML 属性来表示一对多和多对多关联。使用 types: java.util.Collection、java.util.List 或 java.util.Set 表示所有多方关系。 目标实体类型通过 Collection、List 或 Set,或者在 @OneToMany@ManyToMany 注释上显式使用 targetEntity 属性(或 XML 等效项)确定。
在以上示例中,由于很多客户机可能共享一个地址或可能具有多个地址,因此每个客户具有一个地址对象是不实际的。使用多方关联可以更好的解决此情况:
@Entity
public class Customer {
  @Id id;
  @ManyToOne Address homeAddress;
  @ManyToOne Address workAddress;
}

@Entity
public class Address{
  @Id id
  @Basic String city;
  @OneToMany(mappedBy="homeAddress") Collection<Customer> homeCustomers;


  @OneToMany(mappedBy="workAddress", targetEntity=Customer.class) 
				Collection workCustomers;
}
在此示例中,相同实体之间存在两种不同关系:Home 地址和 Work 地址关系。非通用集合用于 workCustomers 属性,以说明在通用项不可用时如何使用 targetEntity 属性。

无类关联

无类实体关联在实体元数据描述符 XML 文件中进行定义,定义方式与基于类的关联的定义方式类似。唯一的差异在于,目标实体不是指向实际类,而是指向用于实体的类名的无类实体标识。

下面是一个示例:

<many-to-one name="department" target-entity="@Department" fetch="EAGER">
      <cascade><cascade-all/></cascade>
</many-to-one>
<one-to-many name="employees" target-entity="@Employee" fetch="LAZY">
      <cascade><cascade-all/></cascade>
</one-to-many>

主键

所有实体必须具有主键,可以是简单(单个属性)键或组合(多个属性)键。键属性通过使用标识注释表示,或在实体 XML 描述符文件中定义。键属性具有以下需求: 组合主键可以选择定义主键类。 实体通过使用 @IdClass 注释或实体 XML 描述符文件与主键类进行关联。@IdClass 注释在与 EntityManager.find 方法结合使用时很有用。
主键类具有以下需求:
  • 它应该是具有无参构造方法的公共类。
  • 主键类的访问类型由声明主键类的实体确定。
  • 如果是属性访问,那么主键类的属性必须为公共或受保护。
  • 主键字段或属性必须匹配引用实体中定义的键属性名称和类型。
  • 主键类必须实现 equalshashCode 方法。
下面是一个示例:
@Entity
@IdClass(CustomerKey.class)
public class Customer {
    @Id @ManyToOne Zone zone;
    @Id int custId;
    String 
 name;    ...
}

@Entity
public class Zone{
    @Id String zoneCode;
    String 
 name;}

public class CustomerKey {
    Zone zone;
    int custId;

    public int hashCode() {...}
    public boolean equals(Object o) {...}
}

无类主键

无类实体需要具有属性 id=true 的 XML 文件中至少具有一个 <id> 元素或关联。 这两者的示例将类似如下:

<id name="serialNumber" type="int"/>
<many-to-one name="department" target-entity="@Department" id="true">
<cascade><cascade-all/></cascade>
</many-to-one>
切记:
无类实体不支持 <id-class> XML 标记。

实体代理和字段拦截

实体类和可变受支持属性类型由代理类(针对属性访问实体)和 bytecode-enhanced 类(针对字段访问实体)扩展。所有对实体的访问(甚至是通过内部业务方法和 equals 方法进行的访问)必须使用相应字段或属性访问方法。

使用代理和字段拦截器,以允许实体管理器跟踪实体的状态,确定实体是否已更改以及改善性能。

注意: 使用属性访问实体时,equals 方法应使用 instanceof 运算符来比较当前实例与输入对象。所有对目标对象执行的检测应检测对象属性,而不是字段本身,这是因为对象实例将为代理。