跳转至内容

Java 持久性/高级主题

来自 Wikibooks,开放世界中的开放书籍

高级主题

[编辑 | 编辑源代码]

事件是系统中的一个钩子,允许在事件发生时执行一些代码。事件可用于扩展、集成、调试、审计或监控系统。

JPA 为Entity对象的持久生命周期定义了多个事件。JPA 事件通过注解或 orm.xml 定义。持久类中的任何方法都可以用事件注解进行注解,以便对该类所有实例进行调用。也可以使用 EntityListeners 注解或<entity-listeners> XML 元素为类配置事件监听器。指定的监听器类不需要实现任何接口(JPA 不使用 Java 事件模型),它只需要用所需的事件注解注解其方法即可。

JPA 定义了以下事件

  • PostLoad - 在将Entity加载到持久化上下文(EntityManager)中之后,或者在refresh操作之后调用。
  • PrePersist - 在对Entity调用persist操作之前调用。对于新实例,在merge期间也调用,以及在persist操作的级联期间调用。对象的Id可能尚未分配,并且可以由事件分配代码。
  • PostPersist - 在将新实例持久化到数据库之后调用。这发生在flushcommit操作期间,在数据库INSERT发生之后,但在事务提交之前。它不会在persist操作期间发生。对象的Id应该分配。
  • PreUpdate - 在将实例更新到数据库之前调用。这发生在flushcommit操作期间,在数据库UPDATE发生之后,但在事务提交之前。它不会在merge操作期间发生。
  • PostUpdate - 在将实例更新到数据库之后调用。这发生在flushcommit操作期间,在数据库UPDATE发生之后,但在事务提交之前。它不会在merge操作期间发生。
  • PreRemove - 在对Entity调用remove操作之前调用。对于remove操作的级联,也调用它。它还在 JPA 2.0 中的flushcommit期间为orphanRemoval调用。
  • PostRemove - 在将实例从数据库中删除之后调用。这发生在flushcommit操作期间,在数据库DELETE发生之后,但在事务提交之前。它不会在remove操作期间发生。

实体事件注解示例

[编辑 | 编辑源代码]
@Entity
public class Employee {
  @Id
  private String uid;
  @Basic
  private Calendar lastUpdated;
  ...

  @PrePersist
  public void prePersist() {
    this.uid = UIDGenerator.newUUI();
    this.lastUpdated = Calendar.getInstance();
  }

  @PreUpdate
  public void preUpdate() {
    this.lastUpdated = Calendar.getInstance();
  }
}

实体监听器事件注解示例

[编辑 | 编辑源代码]
@Entity
@EntityListeners(EmployeeEventListener.class)
public class Employee {
  @Id
  private String uid;
  @Basic
  private Calendar lastUpdated;
  ...
}


public class EmployeeEventListener {
  @PrePersist
  public void prePersist(Object object) {
    Employee employee = (Employee)object;
    employee.setUID(UIDGenerator.newUUI());
    employee.setLastUpdated(Calendar.getInstance());
  }

  @PreUpdate
  public void preUpdate(Object object) {
    Employee employee = (Employee)object;
    employee.setLastUpdated(Calendar.getInstance());
  }
}

实体事件 xml 示例

[编辑 | 编辑源代码]
<entity name="Employee" class="org.acme.Employee" access="FIELD">
    <pre-persist method-name="prePersist"/>
    <pre-update method-name="preUpdate"/>
    <attributes>
        <id name="uid"/>
    </attributes>
</entity>

实体监听器事件 xml 示例

[编辑 | 编辑源代码]
<entity name="Employee" class="org.acme.Employee" access="FIELD">
    <entity-listeners>
        <entity-listener class="org.acme.EmployeeEventListener">
            <pre-persist method-name="prePersist"/>
            <pre-update method-name="preUpdate"/>
        </entity-listener>
    </entity-listeners>
    <attributes>
        <id name="uid"/>
    </attributes>
</entity>

默认实体监听器

[编辑 | 编辑源代码]

也可以配置默认的实体监听器。此监听器将接收持久化单元中所有实体类的事件。默认监听器只能通过 XML 定义。

如果定义了默认的实体监听器,而某个类想要定义自己的监听器,或者不想使用默认监听器,则可以使用 ExcludeDefaultListeners 注解或<exclude-default-listeners> XML 元素禁用它。

默认实体监听器 xml 示例

[编辑 | 编辑源代码]
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings version="2.0"
        xmlns="http://java.sun.com/xml/ns/persistence/orm"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm orm_2_0.xsd">
    <persistence-unit-metadata>
        <persistence-unit-defaults>
            <entity-listeners>
                <entity-listener class="org.acme.ACMEEventListener">
                    <pre-persist method-name="prePersist"/>
                    <pre-update method-name="preUpdate"/>
                </entity-listener>
            </entity-listeners>
        </persistence-unit-defaults>
    </persistence-unit-metadata>
</entity-mappings>

事件和继承

[edit | edit source]

实体监听器是继承的。如果子类不想继承超类实体监听器,则必须定义ExcludeSuperclassListeners注解或<exclude-superclass-listeners> XML 元素。

事件和嵌入式

[edit | edit source]

JPA 并没有为Embeddable定义任何事件。一些 JPA 提供者可能允许为Embeddable对象定义事件。

扩展事件

[edit | edit source]

JPA 事件只定义了实体生命周期。没有 EntityMangager 事件或系统级事件。

一些 JPA 提供者可能提供额外的事件。

TopLink / EclipseLink : 提供扩展事件机制。通过DescriptorEventListener API 定义了额外的Entity级事件。会话级事件机制也通过SessionEventListener API 提供。事件对象还提供了额外的信息,包括数据库行和对象更改集。

视图

[edit | edit source]

数据库VIEW 是表或查询的虚拟视图。视图用于隐藏表、表集或数据集的复杂性。

在 JPA 中,您可以像映射表一样映射到VIEW,使用@Table注解。然后,您可以将视图中的每个列映射到对象的属性。视图通常是只读的,因此对象到视图的映射通常也是只读的。在大多数数据库中,视图也可以是可更新的,这取决于它们封装的查询的复杂程度。即使对于复杂的查询,数据库触发器通常也可以用于更新视图。

视图通常可以在 JPA 中用于解决映射限制。例如,如果 JPA 不支持表联接,或者希望调用数据库函数来转换数据,这通常可以在视图中完成,因此 JPA 就可以只映射到简化的数据。

使用视图确实需要数据库专业知识,并且视图的定义可能依赖于数据库。

接口

[edit | edit source]

接口可以在模型中用于两个主要目的。第一个是作为定义类公共 API 的公共接口。第二个是作为允许多个不同实现者的通用类型。

公共接口

[edit | edit source]

如果在 JPA 中有公共接口,您只需要映射实现类,大部分情况下就可以了。一个问题是,您需要使用实现类进行查询,因为 JPA 不了解接口。对于 JPQL,默认别名也是实现类,但是您可以通过将Entityname设置为公共接口来重新定义它。

一些 JPA 提供者允许定义接口。

TopLink / EclipseLink : 支持使用DescriptorCustomizerInterfacePolicy 定义和查询公共接口。

公共接口别名示例

[edit | edit source]
@Entity(name="Employee")
public class EmployeeImpl implements Employee {
  ...
}

如果您有一个使用公共接口而不是实现的关联关系,那么 JPA 将不知道如何正确映射它。您可以使用targetEntity属性来定义关联关系是到实现类(参见目标实体)。

公共接口关联关系示例

[edit | edit source]
@Entity(name="Employee")
public class EmployeeImpl implements Employee {
  @ManyToOne(targetEntity=EmployeeImpl.class)
  private Employee manager;
}

接口类型

[edit | edit source]

如果您有一个具有多个不同实现者的通用接口,这可能会出现一些问题。如果您使用接口来定义可变关联关系,那么这很难映射。JPA 不直接支持接口或可变关联关系。您可以将接口更改为abstract 类,然后使用TABLE_PER_CLASS 继承来映射它。否则,您可以将关联关系拆分为多个关联关系,每个实现者一个,或者您可以只删除关联关系并查询相关的对象。查询接口也很困难,您需要查询接口的每个实现者,然后在内存中联合结果。

一些 JPA 提供者支持映射接口和可变关联关系。

TopLink / EclipseLink : 支持使用SessionCustomizerRelationalDescriptorInterfacePolicy 映射接口。可变关联关系可以使用@VariableOneToOne 注解或 XML 定义。

存储过程

[edit | edit source]

存储过程是驻留在数据库中的过程或函数。存储过程通常用类似于 SQL 的某些数据库特定语言编写,例如 Oracle 上的 PL/SQL。一些数据库(例如 Oracle)还支持用 Java 编写的存储过程。

存储过程可用于执行批处理数据处理任务。通过在数据库中编写任务,它可以避免将数据发送到数据库客户端和从数据库客户端发送数据的成本,因此可以更有效地运行。存储过程还可以用于访问只能在服务器上访问的数据库特定功能。如果需要严格的安全要求,存储过程也可以使用,以避免授予用户访问原始表或未经验证的 SQL 操作的权限。一些遗留应用程序也使用数据库过程语言编写,需要与它们集成。

使用存储过程的缺点是它们不如使用 SQL 灵活,并且需要开发和维护通常用与应用程序开发人员可能习惯的语言不同的语言编写的功能,并且难以开发和调试,并且通常使用有限的过程编程语言。还有一种普遍的误解,即使用存储过程将提高性能,因为如果您将应用程序正在执行的相同 SQL 放入存储过程,它将以某种方式变得更快。这是错误的,通常情况恰恰相反,因为存储过程限制了持久层动态优化数据检索的能力。只有当存储过程使用比应用程序更优化的 SQL 时,存储过程才会提高性能,通常是在它们在数据库上执行整个任务时。要从应用程序中生成的 SQL 获得最佳性能,您必须使用准备好的语句 - 否则数据库每次提交查询时都必须创建一个新的执行计划。

JPA 2.1 StoredProcedureQuery

[edit | edit source]

JPA 2.1 支持使用StoredProcedureQuery@NamedStoredProcedureQuery 注解或<named-stored-procedure-query> XML 元素来调用数据库存储过程。JPA 支持元数据中定义的命名存储过程调用和通过EntityManager.createNamedStoredProcedureQuery() 创建的命名存储过程调用,以及通过EntityManager.createStoredProcedureQuery() 创建的动态存储过程调用。

StoredProcedureQuery 是一个 JPA 查询,它提供了额外的 API 用于设置存储过程参数,以及用于访问输出参数和多个结果集。StoredProcedureQuery 可以返回实体对象或数据,类似于原生 SQL 查询。ResultSetMapping 可用于将返回的字段映射到实体列。

一些数据库,如 MySQL、SQL Server 和 Sybase,支持返回结果集的存储过程。一些数据库,如 Oracle,不支持返回结果集,但支持返回游标的输出参数。如果存储过程返回结果集或游标输出参数,则可以使用 getResultList()getSingleResult()。如果存储过程具有多个结果集或多个游标输出参数,则再次调用 getResultList() 将返回下一个结果集或游标输出参数。如果存储过程不返回任何内容,则可以使用 executeUpdate()StoredProcedureQuery 还定义了一个 execute() API,该 API 执行过程并返回一个 boolean 值,指示是否返回了结果集。执行后,可以使用 getOutputParameterValue() 访问任何存储过程的输出参数。

存储过程参数通过 @StoredProcedureParameter 注解或 <stored-procedure-parameter> XML 元素定义,或者对于动态存储过程调用,通过 StoredProcedureQuery.registerStoredProcedureParameter() API 定义。参数可以命名或按索引,定义参数的相应 Java 类型,并通过 ParameterMode 定义参数的方向模式。模式可以是 ININOUTOUTREF_CURSOR 之一。对于 ref 游标,可以使用 void.class 类型。

命名存储过程注释示例

[edit | edit source]
// This stored procedure returns a result set and has one input parameter.
@NamedStoredProcedureQuery(
    name = "ReadAddressById",
    resultClasses = Address.class,
    procedureName = "READ_ADDRESS",
    parameters = {
        @StoredProcedureParameter(mode=javax.persistence.ParameterMode.IN, name="P_ADDRESS_ID", type=Long.class)
    }
)
@Entity
public class Address {
  ...
}

调用命名存储过程示例

[edit | edit source]
StoredProcedureQuery query = em.createNamedStoredProcedureQuery("ReadAddressById");
query.setParameter("P_ADDRESS_ID", 12345);
List<Address> result = query.getResultList();

命名游标存储过程注释示例

[edit | edit source]
// This stored procedure returns a cursor output parameter, and has one input parameter.
@NamedStoredProcedureQuery(
    name = "ReadAddressById",
    resultClasses = Address.class,
    procedureName = "READ_ADDRESS",
    parameters = {
        @StoredProcedureParameter(mode=IN, name="P_ADDRESS_ID", type=Long.class),
        @StoredProcedureParameter(mode=REF_CURSOR, name="CUR_ADDRESS", type=void.class)
    }
)
@Entity
public class Address {
  ...
}

调用具有游标输出参数的命名存储过程示例

[edit | edit source]
StoredProcedureQuery query = em.createNamedStoredProcedureQuery("ReadAddressById");
query.setParameter("P_ADDRESS_ID", 12345);
query.execute();
List<Address> result = (List<Address>)query.getOutputParameterValue("CUR_ADDRESS");

调用动态存储过程示例

[edit | edit source]
StoredProcedureQuery query = em.createStoredProcedureQuery("VALIDATE_ADDRESS");
query.registerStoredProcedureParameter("P_COUNTRY", String.class, ParameterMode.IN);
query.registerStoredProcedureParameter("P_PROVINCE", String.class, ParameterMode.IN);
query.registerStoredProcedureParameter("P_CITY", String.class, ParameterMode.IN);
query.registerStoredProcedureParameter("P_STREET", String.class, ParameterMode.IN);
query.registerStoredProcedureParameter("P_POSTAL_CD", String.class, ParameterMode.IN);
query.registerStoredProcedureParameter("OUT_VALID", Boolean.class, ParameterMode.OUT);
query.registerStoredProcedureParameter("OUT_ID", Long.class, ParameterMode.OUT);

query.setParameter("P_COUNTRY", "Canada");
query.setParameter("P_PROVINCE", "ON");
query.setParameter("P_CITY", "Ottawa");
query.setParameter("P_STREET", "99 Bank");
query.setParameter("P_POSTAL_CD", "K2H8L2");

query.execute();
Boolean isValid = (Boolean)query.getOutputParameterValue("OUT_VALID");
Long id = (Long)query.getOutputParameterValue("OUT_ID");

JPA 2.0 中的存储过程

[edit | edit source]

JPA 2.0 不直接支持存储过程。某些类型的存储过程可以通过使用原生查询在 JPA 中执行。JPA 中的原生查询允许执行任何不返回任何内容或返回数据库结果集的 SQL。执行存储过程的语法取决于数据库。JPA 原生 SQL 查询不支持使用 OUTPUTINOUT 参数的存储过程。一些数据库,如 DB2、Sybase 和 SQL Server,允许存储过程返回结果集。Oracle 不允许返回结果集,只允许 OUTPUT 参数,但定义了一个可以作为 OUTPUT 参数返回的 CURSOR 类型。Oracle 还支持存储函数,这些函数可以返回单个值。通常可以使用原生 SQL 查询通过从 Oracle DUAL 表中选择函数值来执行存储函数。

一些 JPA 提供者扩展了对存储过程的支持,一些提供者还支持使用存储过程或自定义 SQL 重写 Entity 的任何 CRUD 操作。一些 JPA 提供者支持 CURSOR OUTPUT 参数。

TopLink / EclipseLink : 使用 @NamedStoredProcedureQuery, @NamedStoredFunctionQuery 注解或 XML,或 StoredProcedureCall, StoredFunctionCall 类支持存储过程和存储函数。还支持使用 DescriptorCustomizerDescriptorQueryManager 类重写类的或关系的任何 CRUD 操作。支持 IN、OUT、INOUT 和 CURSOR OUTPUT 参数。

在 Oracle 上执行存储过程示例

[edit | edit source]
EntityManager em = getEntityManager();
Query query = em.createNativeQuery("BEGIN VALIDATE_EMP(P_EMP_ID=>?); END;");
query.setParameter(1, empId);
query.executeUpdate();

PL/SQL 存储过程

[edit | edit source]

在 Oracle 中,存储过程通常用 Oracle 的 PL/SQL 语言编写。Oracle 中的 PL/SQL 支持一些其他数据类型,Oracle 不通过 SQL 或 JDBC 支持这些数据类型。这些类型包括 BOOLEANTABLERECORD 等类型。从 Java 访问这些类型或过程很困难,因为 JDBC 不支持这些类型。一种解决方法是用普通的存储过程包装 PL/SQL 存储过程,这些存储过程将 PL/SQL 类型转换为标准 SQL/JDBC 类型,如 INTVARRAY (Array) 和 OBJECT TYPE (Struct)。一些 JPA 提供者扩展了对调用 PL/SQL 存储过程的支持。

TopLink / EclipseLink : 使用 @NamedPLSQLStoredProcedureQuery, @NamedPLSQLStoredFunctionQuery 注解或 XML,或 PLSQLStoredProcedureCall, PLSQLStoredFunctionCall 类支持 PL/SQL 存储过程和函数。

结构化对象关系数据类型

[edit | edit source]

在面向对象数据库 (OODBMS) 兴起时,许多关系数据库供应商决定将面向对象的概念添加到关系数据中。这些新的*混合* 数据库被称为对象关系数据库,因为它们可以存储对象和关系数据。这些对象关系数据类型在 SQL3 中被标准化,并且从 Java 中的 JDBC 2.0 API 中添加了对它们的 支持。尽管围绕新的数据形式有很多炒作,但对象关系数据从未流行起来,因为人们似乎更喜欢他们的标准关系数据。通常不建议使用对象关系数据,因为关系数据更标准,但是如果处理非常复杂的数据,它可能是一个可以研究的内容。

一些常见的对象关系数据库功能包括

  • 对象类型(结构)
  • 数组和数组类型
  • 嵌套表
  • 继承
  • 对象 ID (OID)
  • 引用

支持对象关系数据的数据库包括

  • Oracle
  • DB2
  • PostgreSQL

基本模型允许你定义结构或对象类型来表示你的数据,结构可以具有嵌套结构、基本数据的数组或其他结构,以及对其他结构的引用。然后,你可以在一个普通的 关系表列中存储结构,或者创建一个特殊表格来直接存储结构。查询是基本的 SQL,有一些扩展来处理遍历特殊类型。

JPA 不支持对象关系数据类型,但一些 JPA 提供者可能会提供一些支持。

TopLink / EclipseLink : 通过它们的 @Struct, @Structure, @Array 注解和 XML,或它们的 ObjectRelationalDataTypeDescriptor 和映射类支持对象关系数据类型。还为 Oracle 空间数据库 JGeometry 结构和其他结构化数据类型提供了自定义支持,使用 @StructConverter 注解或 XML。

另请参阅,

XML 数据类型

[edit | edit source]

随着 XML 数据库的出现,许多关系数据库决定添加增强的 XML 支持。尽管始终可以使用 VARCHARCLOB 列在关系数据库中存储 XML,但让数据库意识到 XML 数据确实有其优点。主要优点是提供 XML 支持的数据库允许使用 XPath 或 XQuery 语法查询 XML 数据。一些数据库还允许比 Lob 存储更有效地存储 XML 数据。

具有 XML 支持的数据库包括

  • Oracle (XDB)
  • DB2
  • PostgreSQL

JPA 不扩展支持 XML 数据,尽管可以将 XML 字符串存储到数据库中,只需将其映射为 Basic 即可。一些 JPA 提供者可能会提供扩展的 XML 数据支持。例如,查询扩展,或允许映射 XML DOM。

如果你希望将 XML 数据映射到对象,可以使用 JAXB 规范。你甚至可以将它与 JPA 对象集成。

TopLink / EclipseLink : 支持 Oracle XDB XMLType 列,使用它们的 DirectToXMLTypeMapping。XMLType 可以映射为字符串或 XML DOM(文档)。为 Expression 查询中的 XPath 查询提供查询扩展。EclipseLink 还包含用于对象-XML 映射的 JAXB 实现。

过滤器

[编辑 | 编辑源代码]

有时需要从所有查询中过滤掉表中的一些内容。这通常是因为该表由多个类型、应用程序、租户或区域共享,而 JPA 应用程序只对其中一部分行感兴趣。也可能是因为该表包含 JPA 应用程序应忽略的历史记录或存档行。

JPA 未提供任何专门用于过滤数据的支持,但有一些可用选项。继承可用于在行的类型上进行类型检查。例如,如果在 EMPLOYEE 表中有一个 STATUS 列,则可以定义 EmployeeCurrentEmployee 子类,其鉴别器 STATUSACTIVE,并且始终在应用程序中使用 CurrentEmployee。同样,可以定义一个 ACMEEmployee 子类,该子类使用 TENANT 列作为其值为 ACME 的类鉴别器。另一个解决方案是使用数据库视图来过滤数据并将实体映射到这些视图。

这些解决方案不适用于动态过滤,在这种过滤中,过滤条件参数在运行时之前未知(例如租户或区域)。此外,无法通过继承来建模复杂条件,尽管数据库视图仍然应该有效。一种解决方案是始终将条件追加到任何查询中,例如将 JPQL 字符串或 JPA 条件追加到应用程序代码中。

虚拟专用数据库 (VPD) 支持也可能提供解决方案。一些数据库(如 Oracle)支持 VPD,允许根据连接的用户或代理证书对行进行基于上下文的过滤。

一些 JPA 提供程序对过滤数据有特定支持。

TopLink / EclipseLink : 通过其 @AdditionalCriteria 注解和 XML 支持过滤数据。这允许将任意 JPQL 片段追加到该实体的所有查询中。该片段可以包含可以通过持久性单元或上下文属性在运行时设置的参数。还支持 Oracle VPD,包括 Oracle 代理身份验证和隔离数据。

历史记录

[编辑 | 编辑源代码]

数据库应用程序中的一种常见需求是维护数据库更改的记录和历史记录。这可用于跟踪和审计目的,或允许撤消更改,或保留系统数据随时间的记录。许多数据库都具有审计功能,允许对数据进行的更改进行一定程度的跟踪。一些数据库(如 Oracle 的 Flashback 功能)允许在行级别自动跟踪历史记录,甚至允许查询数据的过去版本。

应用程序也可以通过其数据模型来维护自己的历史记录。所需的一切是在表中添加一个 STARTEND 时间戳列。然后,当前 行是 END 时间戳为 null 的行。当插入一行时,其 START 时间戳将设置为当前时间。当更新一行时,系统将插入一个具有相同 id 和数据的新行,但具有不同的 START 时间戳,并将旧行更新为将其 END 时间戳设置为当前时间。该表的主键需要添加 START 时间戳。

历史记录还可以用于避免删除。可以仅将 END 时间戳设置为当前时间,而不是删除一行。另一个解决方案是向表中添加一个 DELETED 布尔列,并在删除一行时进行记录。

历史记录数据可以存储在修改后的表中,也可以将表保留为仅包含数据的当前版本,并添加一个镜像历史记录表来存储数据。在镜像情况下,可以使用数据库触发器写入历史记录表。对于就地情况,可以使用数据库视图来提供表在当前时间的视图。

要从历史记录表中查询当前数据,任何查询都必须包含 ENDNULL 的子句。要查询特定时间点,where 子句必须包含该时间点位于 STARTEND 时间戳之间的条件。

JPA 未定义任何特定的历史记录支持。

可以使用 JPA 使用 Oracle 回闪,但任何针对历史数据的查询都需要使用本机 SQL。

如果使用具有触发器的镜像历史记录表,JPA 仍然可以用于查询当前数据。也可以将子类或兄弟类映射到历史记录表,以允许查询历史记录数据。

如果使用数据库视图,则可以通过将 Entity 映射到该视图来使用 JPA。

如果使用历史记录表,JPA 仍然可以用于映射到该表,并且可以向对象添加 startend 属性。针对当前数据的查询可以将当前时间追加到查询中。关系更难处理,因为 JPA 要求关系按主键进行,而历史记录关系则不会。

一些 JPA 提供程序支持历史记录。

TopLink / EclipseLink : 支持 Oracle 回闪查询以及应用程序特定的历史记录。可以使用查询提示 "eclipselink.history.as-of"Expression 查询来定义历史查询。还支持使用 HistoryPolicy API 自动跟踪历史记录,该 API 支持维护和查询镜像历史记录表。

逻辑删除

[编辑 | 编辑源代码]

请参阅 审计和安全

数据复制可用于备份数据,以提高容错能力和故障转移,或用于负载均衡和扩展数据库。

对于复制,更改将写入多个数据库,这些更改可以由应用程序、JPA 提供程序或数据库后端进行。对于故障转移,如果其中一个数据库出现故障,则可以使用另一个数据库,而不会丢失数据或应用程序停机时间。对于负载均衡,读取请求可以在复制的数据库之间进行负载均衡,以减少每个数据库上的负载,并提高应用程序的可扩展性。

大多数企业数据库都支持某种形式的自动备份或复制。诸如 Oracle RAC 之类的集群数据库还允许负载均衡、故障转移和高可用性。如果您的数据库支持复制或集群,则它通常对 JPA 是透明的。可能需要使用专门的 DataSource(如 Oracle UCP 或 WebLogic GridLink)来处理负载均衡和故障转移。

JPA 未定义任何特定的数据复制支持,但一些 JPA 提供程序提供复制支持。如果您的数据库不支持复制,则可以通过拥有多个持久性单元并将对象持久化和合并到两个数据库中来自己实现复制。

TopLink / EclipseLink : 支持复制、负载均衡和故障转移。复制和负载均衡通过 EclipseLink 的分区支持(使用 @ReplicationPartitioning@RoundRobinPartitioning 注解和 XML)来支持。

数据分区可用于在多个数据库机器上扩展应用程序,或与 Oracle RAC 之类的集群数据库一起使用。

分区将您的数据拆分到每个数据库节点中。有水平分区和垂直分区。垂直分区通常是最容易实现的。您只需将一半类放在一个数据库中,而另一半放在另一个数据库中。理想情况下,这两个集合彼此隔离,并且没有任何跨数据库关系。这可以通过 JPA 直接完成,方法是为每个数据库创建两个不同的持久性单元。

对于水平分区,您需要将数据拆分到多个数据库节点中。每个数据库节点将具有相同的表,但每个节点的表将仅存储一部分数据。您可以按数据值对数据进行分区,例如范围分区、值分区、哈希分区,甚至循环分区。JPA 未定义任何数据分区支持,因此您要么需要为每个分区定义一个不同的类,要么使用 JPA 供应商特定的功能。

TopLink / EclipseLink : 支持水平和垂直数据分区。在会话、实体和查询级别支持哈希、值、范围、固定和自定义分区。分区通过 @Partitioning, @HashPartitioning, @RangePartitioning, @ValuePartitioning, @PinnedPartitioning@Partitioned 注解和 XML 来支持。

另请参阅,

数据集成

[编辑 | 编辑源代码]

NoSQL(以及 EIS、传统、XML 和非关系型数据)

[编辑 | 编辑源代码]

NoSQL

多租户

[编辑 | 编辑源代码]

动态数据

[编辑 | 编辑源代码]
华夏公益教科书