Usar extensões para personalizar as operações do Cliente Aprimorado do DynamoDB
A API do Cliente Aprimorado do DynamoDB fornece suporte a extensões de complemento que fornecem funcionalidades além das operações de mapeamento. As extensões usam dois métodos de hook para modificar dados durante as operações de leitura e gravação:
beforeWrite(): modifica uma operação de gravação antes que ela aconteçaafterRead(): modifica os resultados de uma operação de leitura depois que ela acontece
Algumas operações (como atualizações de itens) realizam uma gravação e depois uma leitura, então os dois métodos de hook são chamados.
Como as extensões são carregadas
As extensões são carregadas na ordem em que você especifica no compilador do cliente aprimorado. A ordem de carregamento pode ser importante porque uma extensão pode atuar em valores que foram transformados por uma extensão anterior.
Por padrão, o cliente aprimorado carrega duas extensões:
VersionedRecordExtension: proporciona um bloqueio positivoAtomicCounterExtension: incrementa automaticamente os atributos do contador
Você pode substituir o comportamento padrão com o compilador de cliente aprimorado e carregar qualquer extensão. Você também pode especificar nenhuma se não quiser as extensões padrão.
Importante
Se você carregar suas próprias extensões, o cliente aprimorado não carregará nenhuma extensão padrão. Se você quiser o comportamento fornecido por uma extensão padrão, precisará adicioná-la explicitamente à lista de extensões.
O exemplo a seguir mostra como carregar uma extensão personalizada chamada verifyChecksumExtension depois de VersionedRecordExtension. A AtomicCounterExtension não está carregada neste exemplo.
DynamoDbEnhancedClientExtension versionedRecordExtension = VersionedRecordExtension.builder().build(); DynamoDbEnhancedClient enhancedClient = DynamoDbEnhancedClient.builder() .dynamoDbClient(dynamoDbClient) .extensions(versionedRecordExtension, verifyChecksumExtension) .build();
Detalhes e configuração da extensão disponível
As seções a seguir fornecem informações detalhadas sobre cada extensão disponível no SDK.
Implementar um bloqueio positivo com o VersionedRecordExtension
A extensão VersionedRecordExtension fornece bloqueio positivo incrementando e rastreando um número de versão do item à medida que os itens são gravados no banco de dados. É adicionada uma condição a cada operação de gravação que faz com que ela falhe caso o número de versão do item armazenado não corresponda ao valor que a aplicação leu por último.
Configuração
Para especificar qual atributo usar para rastrear o número da versão do item, marque um atributo numérico no esquema da tabela.
O trecho a seguir especifica que o atributo version deve conter o número da versão do item.
@DynamoDbVersionAttribute public Integer getVersion() {...}; public void setVersion(Integer version) {...};
A abordagem equivalente do esquema de tabela estática é mostrada no trecho a seguir.
.addAttribute(Integer.class, a -> a.name("version") .getter(Customer::getVersion) .setter(Customer::setVersion) // Apply the 'version' tag to the attribute. .tags(VersionedRecordExtension.AttributeTags.versionAttribute())
Como funciona
O bloqueio positivo com a VersionedRecordExtension tem o seguinte impacto sobre estes métodos DynamoDbEnhancedClient e DynamoDbTable:
putItem-
Novos itens recebem um valor de versão inicial de 0. Isso pode ser configurado com
@DynamoDbVersionAttribute(startAt =.X) updateItem-
Se você recuperar um item, atualizar uma ou mais das suas propriedades e tentar salvar as alterações, a operação será bem-sucedida somente se o número de versão no lado do cliente e no lado do servidor corresponder.
Se for bem-sucedida, o número da versão é incrementado automaticamente em 1. Isso pode ser configurado com
@DynamoDbVersionAttribute(incrementBy =.X) deleteItem-
A anotação
DynamoDbVersionAttributenão tem efeito. Você deve adicionar uma expressão de condição manualmente ao excluir um item.O exemplo a seguir adiciona uma expressão condicional para garantir que o item excluído seja o que foi lido. No exemplo a seguir,
recordVersioné o atributo do bean anotado com@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: esse método tem o mesmo comportamento deputItem. -
addUpdateItem: esse método tem o mesmo comportamento deupdateItem. -
addDeleteItem: esse método tem o mesmo comportamento dedeleteItem.
-
batchWriteItem-
-
addPutItem: esse método tem o mesmo comportamento deputItem. -
addDeleteItem: esse método tem o mesmo comportamento dedeleteItem.
-
nota
As tabelas globais do DynamoDB usam uma reconciliação do tipo “o último a gravar vence” entre as atualizações simultâneas. Com ela, o DynamoDB faz o melhor esforço para determinar o último a gravar. Se você usa tabelas globais, essa política de “o último a gravar vence” significa que as estratégias de bloqueio podem não funcionar conforme o esperado, pois todas as réplicas acabarão por convergir com base na última gravação determinada pelo DynamoDB.
Como desabilitar
Para desabilitar o bloqueio positivo, não use a anotação @DynamoDbVersionAttribute.
Implementar contadores com a AtomicCounterExtension
A extensão AtomicCounterExtension incrementa um atributo numérico marcado sempre que um registro é gravado no banco de dados. É possível especificar valores de início e de incremento. Se nenhum valor for especificado, o valor inicial será definido como 0 e o valor do atributo será incrementado em 1.
Configuração
Para especificar qual atributo é um contador, marque um atributo do tipo Long no esquema da tabela.
O trecho a seguir mostra o uso dos valores padrão de início e de incremento para o atributo counter.
@DynamoDbAtomicCounter public Long getCounter() {...}; public void setCounter(Long counter) {...};
A abordagem do esquema de tabela estática é mostrada no trecho a seguir. A extensão do contador atômico usa um valor inicial de 10 e incrementa o valor em 5 cada vez que o registro é gravado.
.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))
Adicionar carimbos de data e hora com a AutoGeneratedTimestampRecordExtension
A extensão AutoGeneratedTimestampRecordExtension atualiza automaticamente os atributos marcados do tipo Instant com um carimbo de data e hora atual sempre que o item é gravado com sucesso no banco de dados. Essa extensão não é carregada por padrão.
Configuração
Para especificar qual atributo atualizar com o carimbo de data/hora atual, marque o atributo Instant no esquema da tabela.
O atributo lastUpdate é o alvo do comportamento da extensão no trecho a seguir. Observe a exigência de que o atributo seja um tipo Instant.
@DynamoDbAutoGeneratedTimestampAttribute public Instant getLastUpdate() {...} public void setLastUpdate(Instant lastUpdate) {...}
A abordagem equivalente do esquema de tabela estática é mostrada no trecho a seguir.
.addAttribute(Instant.class, a -> a.name("lastUpdate") .getter(Customer::getLastUpdate) .setter(Customer::setLastUpdate) // Applying the 'autoGeneratedTimestamp' tag to the attribute. .tags(AutoGeneratedTimestampRecordExtension.AttributeTags.autoGeneratedTimestampAttribute())
Gerar um UUID com a AutoGeneratedUuidExtension
A extensão AutoGeneratedUuidExtension gera um UUID (Identificador Único Universal) exclusivo para um atributo quando um novo registro é gravado no banco de dados. Usa o método Java JDK UUID.randomUUID()java.lang.String. Essa extensão não é carregada por padrão.
Configuração
O atributo uniqueId é o alvo do comportamento da extensão no trecho a seguir.
@AutoGeneratedUuidExtension public String getUniqueId() {...} public void setUniqueId(String uniqueId) {...}
A abordagem equivalente do esquema de tabela estática é mostrada no trecho a seguir.
.addAttribute(String.class, a -> a.name("uniqueId") .getter(Customer::getUniqueId) .setter(Customer::setUniqueId) // Applying the 'autoGeneratedUuid' tag to the attribute. .tags(AutoGeneratedUuidExtension.AttributeTags.autoGeneratedUuidAttribute())
Se você quiser que a extensão preencha o UUID somente para métodos putItem e não para métodos updateItem, adicione a anotação de comportamento de atualização
@AutoGeneratedUuidExtension @DynamoDbUpdateBehavior(UpdateBehavior.WRITE_IF_NOT_EXISTS) public String getUniqueId() {...} public void setUniqueId(String uniqueId) {...}
Se você usar a abordagem de esquema de tabela estática, use o código equivalente a seguir.
.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))