使用扩展自定义 DynamoDB 增强型客户端操作
DynamoDB 增强型客户端 API 支持插件扩展,这些插件扩展提供的功能不仅限于映射操作。扩展使用两种钩子方法在读取和写入操作期间修改数据:
beforeWrite()- 在写入操作实际执行之前对其进行修改afterRead()- 在读取操作完成之后对返回的结果进行修改
某些操作(例如项目更新)同时执行写入和读取,因此会调用两种钩子方法。
如何加载扩展
按照您在增强型客户端生成器中指定的顺序加载扩展。加载顺序可能很重要,因为一个扩展可以对前一个扩展变换过的值起作用。
默认情况下,增强版客户端会加载两个扩展:
VersionedRecordExtension- 提供乐观锁AtomicCounterExtension- 自动增加计数器属性
您可以使用增强型客户端生成器覆盖默认行为并加载任何扩展。如果您不想使用默认扩展,也可以指定一个都不用。
重要
如果您加载自己的扩展,则增强型客户端不会加载任何默认扩展。如果您想要任一默认扩展提供的行为,则需要将其明确添加到扩展列表中。
以下示例说明如何在 VersionedRecordExtension 之后加载名为 verifyChecksumExtension 的自定义扩展。本示例中未加载 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 实现的乐观锁机制会对这些 DynamoDbEnhancedClient 和 DynamoDbTable 方法产生以下影响:
putItem-
为新项目分配的初始版本值为 0。可以使用
@DynamoDbVersionAttribute(startAt =进行配置。X) updateItem-
如果您检索项目,然后更新它的一个或多个属性,并尝试保存所做更改,那么只有在客户端和服务器端的版本号匹配时操作才能成功。
如果成功,版本号自动加 1。可以使用
@DynamoDbVersionAttribute(incrementBy =进行配置。X) deleteItem-
DynamoDbVersionAttribute注释没有效果。删除项目时,必须手动添加条件表达式。以下示例添加了一个条件表达式,以确保删除的项目是读取的项目。在以下示例中,
recordVersion是 bean 使用@DynamoDbVersionAttribute进行注释的属性。// 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 添加时间戳
每次成功将项目写入数据库时,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())
使用 AutoGeneratedUuidExtension 生成 UUID
向数据库写入新记录时,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))