使用扩展 - Amazon SDK for Java 2.x
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

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

使用扩展

DynamoDB 增强型客户端 API 支持插件扩展,这些插件扩展提供的功能不仅限于映射操作。扩展有两种钩子方法,beforeWrite()afterRead()beforeWrite() 在写入操作发生之前对其进行修改,该 afterRead() 方法在读取操作发生后修改其结果。由于某些操作(例如项目更新)同时执行写入和读取,因此两种钩子方法都会被调用。

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

增强型客户端 API 附带了一组插件扩展,位于 extensions 包中。默认情况下,增强型客户端会加载 VersionedRecordExtensionAtomicCounterExtension。您可以使用增强型客户端生成器覆盖默认行为并加载任何扩展。如果您不想使用默认扩展,也可以指定一个都不用。

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

在以下示例中,名为 verifyChecksumExtension 的自定义扩展是在 VersionedRecordExtension 之后加载的,该扩展通常在默认情况下自行加载。本示例中未加载 AtomicCounterExtension

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

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())

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

每次成功将项目写入数据库时,AutoGeneratedTimestampRecordExtension 都会自动使用当前时间戳更新类型为 Instant 的已标记属性。

默认情况下不加载此扩展。因此,在构建增强型客户端时,您需要将其指定为自定义扩展,如本主题的第一个示例所示。

要指定将使用当前时间戳更新的属性,请在表架构中标记 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())

自定义扩展

以下自定义扩展类显示了使用更新表达式的 beforeWrite() 方法。在注释行 2 之后,如果数据库中的项目还没有 registrationDate 属性,我们会创建一个 SetAction 来设置 registrationDate 属性。每当更新 Customer 对象时,扩展都会确保设置了 registrationDate

public final class CustomExtension implements DynamoDbEnhancedClientExtension { // 1. In a custom extension, use an UpdateExpression to define what action to take before // an item is updated. @Override public WriteModification beforeWrite(DynamoDbExtensionContext.BeforeWrite context) { if ( context.operationContext().tableName().equals("Customer") && context.operationName().equals(OperationName.UPDATE_ITEM)) { return WriteModification.builder() .updateExpression(createUpdateExpression()) .build(); } return WriteModification.builder().build(); // Return an "empty" WriteModification instance if the extension should not be applied. // In this case, if the code is not updating an item on the Customer table. } private static UpdateExpression createUpdateExpression() { // 2. Use a SetAction, a subclass of UpdateAction, to provide the values in the update. SetAction setAction = SetAction.builder() .path("registrationDate") .value("if_not_exists(registrationDate, :regValue)") .putExpressionValue(":regValue", AttributeValue.fromS(Instant.now().toString())) .build(); // 3. Build the UpdateExpression with one or more UpdateAction. return UpdateExpression.builder() .addAction(setAction) .build(); } }