使用扩展程序自定义 DynamoDB 增强型客户端操作 - AWS SDK for Java 2.x

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

使用扩展程序自定义 DynamoDB 增强型客户端操作

DynamoDB 增强型客户端 API 支持插件扩展,这些插件扩展提供的功能不仅限于映射操作。扩展使用两种挂钩方法在读取和写入操作期间修改数据:

  • beforeWrite()-在写入操作发生之前对其进行修改

  • afterRead()-在读取操作发生后修改其结果

有些操作(例如项目更新)同时执行写入和读取,因此两种挂钩方法都会被调用。

扩展程序是如何加载的

扩展按您在增强型客户端生成器中指定的顺序加载。加载顺序可能很重要,因为一个扩展可以对前一个扩展变换过的值起作用。

默认情况下,增强版客户端会加载两个扩展:

您可以使用增强的客户端生成器覆盖默认行为并加载任何扩展。如果您不想使用默认扩展,也可以指定一个都不用。

重要

如果您加载自己的扩展,则增强型客户端不会加载任何默认扩展。如果您想要任一默认扩展提供的行为,则需要将其明确添加到扩展列表中。

以下示例说明如何加载以verifyChecksumExtension命名的自定义扩展模块VersionedRecordExtension。本示例中未加载 AtomicCounterExtension

DynamoDbEnhancedClientExtension versionedRecordExtension = VersionedRecordExtension.builder().build(); DynamoDbEnhancedClient enhancedClient = DynamoDbEnhancedClient.builder() .dynamoDbClient(dynamoDbClient) .extensions(versionedRecordExtension, verifyChecksumExtension) .build();

可用的扩展详细信息和配置

以下各节提供有关 SDK 中每个可用扩展的详细信息。

使用实现乐观锁定 VersionedRecordExtension

VersionedRecordExtension扩展通过在项目写入数据库时递增和跟踪项目版本号来提供乐观锁定。如果实际持久化项目的版本号与应用程序上次读取的值不匹配,则每次写入都会添加一个导致写入失败的条件。

配置

要指定使用哪个属性来跟踪项目版本号,请在表架构中标记数字属性。

以下代码段指定 version 属性应包含项目版本号。

@DynamoDbVersionAttribute public Integer getVersion() {...}; public void setVersion(Integer version) {...};

下面的代码段显示了等效的静态表架构方法。

.addAttribute(Integer.class, a -> a.name("version") .getter(Customer::getVersion) .setter(Customer::setVersion) // Apply the 'version' tag to the attribute. .tags(VersionedRecordExtension.AttributeTags.versionAttribute())

工作方式

使用乐观锁定会VersionedRecordExtension对这些DynamoDbEnhancedClientDynamoDbTable方法产生以下影响:

putItem

新项目的初始版本值为 0。这可以用进行配置@DynamoDbVersionAttribute(startAt = X)

updateItem

如果您检索项目、更新其一个或多个属性并尝试保存更改,则只有在客户端和服务器端的版本号匹配时,操作才会成功。

如果成功,版本号将自动递增 1。这可以用进行配置@DynamoDbVersionAttribute(incrementBy = X)

deleteItem

DynamoDbVersionAttribute注释无效。删除项目时,必须手动添加条件表达式。

以下示例添加了一个条件表达式,以确保删除的项目是已读取的项目。在以下示例中,recordVersion是用@DynamoDbVersionAttribute注释的 bean 的属性。

// 1. Read the item and get its current version. Customer item = customerTable.getItem(Key.builder().partitionValue("someId").build()); // `recordVersion` is the bean's attribute that is annotated with `@DynamoDbVersionAttribute`. AttributeValue currentVersion = item.getRecordVersion(); // 2. Create conditional delete with the `currentVersion` value. DeleteItemEnhancedRequest deleteItemRequest = DeleteItemEnhancedRequest.builder() .key(KEY) .conditionExpression(Expression.builder() .expression("recordVersion = :current_version_value") .putExpressionValue(":current_version_value", currentVersion) .build()).build(); customerTable.deleteItem(deleteItemRequest);
transactWriteItems
  • addPutItem:此方法的行为与putItem

  • addUpdateItem:此方法的行为与updateItem

  • addDeleteItem:此方法的行为与deleteItem

batchWriteItem
  • addPutItem:此方法的行为与putItem

  • addDeleteItem:此方法的行为与deleteItem

注意

DynamoDB 全局表在并发更新之间使用 “最后一个写入者获胜” 对账,其中 DynamoDB 会尽最大努力确定最后一个写入者。如果您使用全局表,这个 “最后一个写入者获胜” 策略意味着锁定策略可能无法按预期起作用,因为所有副本最终都将根据DynamoDB确定的上次写入时间进行收集。

如何禁用

要禁用乐观锁定,请不要使用@DynamoDbVersionAttribute注释。

使用实现计数器 AtomicCounterExtension

每次向数据库写入记录时,AtomicCounterExtension扩展名都会增加一个带标签的数字属性。您可以指定起始值和增量值。如果未指定任何值,则将起始值设置为 0,属性的值以 1 为增量。

配置

要指定哪个属性是计数器,请在表架构中标记一个类型为 Long 的属性。

以下代码段显示了为 counter 属性使用默认起始值和增量值的情况。

@DynamoDbAtomicCounter public Long getCounter() {...}; public void setCounter(Long counter) {...};

下面的代码段显示了静态表架构方法。原子计数器扩展使用起始值 10,并在每次写入记录时将该值递增 5。

.addAttribute(Integer.class, a -> a.name("counter") .getter(Customer::getCounter) .setter(Customer::setCounter) // Apply the 'atomicCounter' tag to the attribute with start and increment values. .tags(StaticAttributeTags.atomicCounter(10L, 5L))

使用添加时间戳 AutoGeneratedTimestampRecordExtension

每次成功将项目写入数据库时InstantAutoGeneratedTimestampRecordExtension扩展程序都会自动使用当前时间戳更新该类型的标记属性。默认情况下不加载此扩展。

配置

要指定将使用当前时间戳更新的属性,请在表架构中标记 Instant 属性。

lastUpdate属性是以下代码段中扩展程序行为的目标。请注意该属性必须是 Instant 类型的要求。

@DynamoDbAutoGeneratedTimestampAttribute public Instant getLastUpdate() {...} public void setLastUpdate(Instant lastUpdate) {...}

下面的代码段显示了等效的静态表架构方法。

.addAttribute(Instant.class, a -> a.name("lastUpdate") .getter(Customer::getLastUpdate) .setter(Customer::setLastUpdate) // Applying the 'autoGeneratedTimestamp' tag to the attribute. .tags(AutoGeneratedTimestampRecordExtension.AttributeTags.autoGeneratedTimestampAttribute())

使用生成一个 UUID AutoGeneratedUuidExtension

当向数据库写入新记录时,AutoGeneratedUuidExtension扩展程序会为属性生成一个唯一的 UUID(通用唯一标识符)。使用 Java JDK uuid.randomuuid () 方法并应用于类型的属性。java.lang.String默认情况下不加载此扩展。

配置

uniqueId属性是以下代码段中扩展程序行为的目标。

@AutoGeneratedUuidExtension public String getUniqueId() {...} public void setUniqueId(String uniqueId) {...}

下面的代码段显示了等效的静态表架构方法。

.addAttribute(String.class, a -> a.name("uniqueId") .getter(Customer::getUniqueId) .setter(Customer::setUniqueId) // Applying the 'autoGeneratedUuid' tag to the attribute. .tags(AutoGeneratedUuidExtension.AttributeTags.autoGeneratedUuidAttribute())

如果您希望扩展程序仅为putItem方法填充UUID,而不为updateItem方法填充 UUID,请添加更新行为注释,如以下代码段所示。

@AutoGeneratedUuidExtension @DynamoDbUpdateBehavior(UpdateBehavior.WRITE_IF_NOT_EXISTS) public String getUniqueId() {...} public void setUniqueId(String uniqueId) {...}

如果您使用静态表架构方法,请使用以下等效代码。

.addAttribute(String.class, a -> a.name("uniqueId") .getter(Customer::getUniqueId) .setter(Customer::setUniqueId) // Applying the 'autoGeneratedUuid' tag to the attribute. .tags(AutoGeneratedUuidExtension.AttributeTags.autoGeneratedUuidAttribute(), StaticAttributeTags.updateBehavior(UpdateBehavior.WRITE_IF_NOT_EXISTS))