-
Notifications
You must be signed in to change notification settings - Fork 993
Optimistic Locking for Delete Operations #6747
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 2 commits
a1f9cfd
adb11e3
c002f9e
5adbe18
cc414f3
3a84c27
fd025d1
b151dfd
e49d02a
e28e78a
31e5aca
48deb28
f925da3
f241c81
efa9521
2176326
1796ed7
f0c0da0
6406ad0
49d2ede
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -245,6 +245,10 @@ default T deleteItem(T keyItem) { | |
| throw new UnsupportedOperationException(); | ||
| } | ||
|
|
||
| default T deleteItem(T keyItem, boolean useOptimisticLocking) { | ||
|
||
| throw new UnsupportedOperationException(); | ||
|
||
| } | ||
|
|
||
| /** | ||
| * Deletes a single item from the mapped table using a supplied primary {@link Key}. | ||
| * <p> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,6 +16,7 @@ | |
| package software.amazon.awssdk.enhanced.dynamodb.internal.client; | ||
|
|
||
| import static software.amazon.awssdk.enhanced.dynamodb.internal.EnhancedClientUtils.createKeyFromItem; | ||
| import static software.amazon.awssdk.enhanced.dynamodb.model.OptimisticLockingHelper.conditionallyApplyOptimisticLocking; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.concurrent.CompletableFuture; | ||
|
|
@@ -124,28 +125,55 @@ public CompletableFuture<Void> createTable() { | |
| .build()); | ||
| } | ||
|
|
||
| /** | ||
| * Supports optimistic locking via {@link software.amazon.awssdk.enhanced.dynamodb.model.OptimisticLockingHelper}. | ||
| */ | ||
| @Override | ||
| public CompletableFuture<T> deleteItem(DeleteItemEnhancedRequest request) { | ||
| TableOperation<T, ?, ?, DeleteItemEnhancedResponse<T>> operation = DeleteItemOperation.create(request); | ||
| return operation.executeOnPrimaryIndexAsync(tableSchema, tableName, extension, dynamoDbClient) | ||
| .thenApply(DeleteItemEnhancedResponse::attributes); | ||
| } | ||
|
|
||
| /** | ||
| * Supports optimistic locking via {@link software.amazon.awssdk.enhanced.dynamodb.model.OptimisticLockingHelper}. | ||
| */ | ||
| @Override | ||
| public CompletableFuture<T> deleteItem(Consumer<DeleteItemEnhancedRequest.Builder> requestConsumer) { | ||
| DeleteItemEnhancedRequest.Builder builder = DeleteItemEnhancedRequest.builder(); | ||
| requestConsumer.accept(builder); | ||
| return deleteItem(builder.build()); | ||
| } | ||
|
|
||
| /** | ||
| * Does not support optimistic locking. Use {@link #deleteItem(Object, boolean)} for optimistic locking support. | ||
| */ | ||
| @Override | ||
| public CompletableFuture<T> deleteItem(Key key) { | ||
| return deleteItem(r -> r.key(key)); | ||
| } | ||
|
|
||
| /** | ||
| * @deprecated Use {@link #deleteItem(Object, boolean)} instead to explicitly control optimistic locking behavior. | ||
| */ | ||
| @Override | ||
| @Deprecated | ||
| public CompletableFuture<T> deleteItem(T keyItem) { | ||
| return deleteItem(keyFrom(keyItem)); | ||
| return deleteItem(keyItem, false); | ||
| } | ||
|
|
||
| /** | ||
| * Deletes an item from the table with optional optimistic locking. | ||
| * | ||
| * @param keyItem the item containing the key to delete | ||
| * @param useOptimisticLocking if true, applies optimistic locking if the item has version information | ||
| * @return a CompletableFuture containing the deleted item, or null if the item was not found | ||
| */ | ||
| @Override | ||
| public CompletableFuture<T> deleteItem(T keyItem, boolean useOptimisticLocking) { | ||
| DeleteItemEnhancedRequest request = DeleteItemEnhancedRequest.builder().key(keyFrom(keyItem)).build(); | ||
| request = conditionallyApplyOptimisticLocking(request, keyItem, tableSchema, useOptimisticLocking); | ||
|
||
| return deleteItem(request); | ||
| } | ||
|
|
||
| @Override | ||
|
|
@@ -311,7 +339,7 @@ public CompletableFuture<T> updateItem(T item) { | |
| public Key keyFrom(T item) { | ||
| return createKeyFromItem(item, tableSchema, TableMetadata.primaryIndexName()); | ||
| } | ||
|
|
||
|
|
||
| @Override | ||
| public CompletableFuture<Void> deleteTable() { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,6 +16,7 @@ | |
| package software.amazon.awssdk.enhanced.dynamodb.internal.client; | ||
|
|
||
| import static software.amazon.awssdk.enhanced.dynamodb.internal.EnhancedClientUtils.createKeyFromItem; | ||
| import static software.amazon.awssdk.enhanced.dynamodb.model.OptimisticLockingHelper.conditionallyApplyOptimisticLocking; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.function.Consumer; | ||
|
|
@@ -126,27 +127,54 @@ public void createTable() { | |
| .build()); | ||
| } | ||
|
|
||
| /** | ||
| * Supports optimistic locking via {@link software.amazon.awssdk.enhanced.dynamodb.model.OptimisticLockingHelper}. | ||
| */ | ||
| @Override | ||
| public T deleteItem(DeleteItemEnhancedRequest request) { | ||
| TableOperation<T, ?, ?, DeleteItemEnhancedResponse<T>> operation = DeleteItemOperation.create(request); | ||
| return operation.executeOnPrimaryIndex(tableSchema, tableName, extension, dynamoDbClient).attributes(); | ||
| } | ||
|
|
||
| /** | ||
| * Supports optimistic locking via {@link software.amazon.awssdk.enhanced.dynamodb.model.OptimisticLockingHelper}. | ||
| */ | ||
| @Override | ||
| public T deleteItem(Consumer<DeleteItemEnhancedRequest.Builder> requestConsumer) { | ||
| DeleteItemEnhancedRequest.Builder builder = DeleteItemEnhancedRequest.builder(); | ||
| requestConsumer.accept(builder); | ||
| return deleteItem(builder.build()); | ||
| } | ||
|
|
||
| /** | ||
| * Does not support optimistic locking. Use {@link #deleteItem(Object, boolean)} for optimistic locking support. | ||
| */ | ||
| @Override | ||
| public T deleteItem(Key key) { | ||
| return deleteItem(r -> r.key(key)); | ||
| } | ||
|
|
||
| /** | ||
| * @deprecated Use {@link #deleteItem(Object, boolean)} instead to explicitly control optimistic locking behavior. | ||
| */ | ||
| @Override | ||
| @Deprecated | ||
| public T deleteItem(T keyItem) { | ||
| return deleteItem(keyFrom(keyItem)); | ||
| return deleteItem(keyItem, false); | ||
|
||
| } | ||
|
|
||
| /** | ||
| * Deletes an item from the table with optional optimistic locking. | ||
| * | ||
| * @param keyItem the item containing the key to delete | ||
| * @param useOptimisticLocking if true, applies optimistic locking if the item has version information | ||
| * @return the deleted item, or null if the item was not found | ||
| */ | ||
| @Override | ||
| public T deleteItem(T keyItem, boolean useOptimisticLocking) { | ||
|
||
| DeleteItemEnhancedRequest request = DeleteItemEnhancedRequest.builder().key(keyFrom(keyItem)).build(); | ||
| request = conditionallyApplyOptimisticLocking(request, keyItem, tableSchema, useOptimisticLocking); | ||
| return deleteItem(request); | ||
| } | ||
|
|
||
| @Override | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,6 +15,8 @@ | |
|
|
||
| package software.amazon.awssdk.enhanced.dynamodb.model; | ||
|
|
||
| import static software.amazon.awssdk.enhanced.dynamodb.model.OptimisticLockingHelper.createVersionCondition; | ||
|
|
||
| import java.util.Objects; | ||
| import java.util.function.Consumer; | ||
| import software.amazon.awssdk.annotations.NotThreadSafe; | ||
|
|
@@ -24,6 +26,7 @@ | |
| import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable; | ||
| import software.amazon.awssdk.enhanced.dynamodb.Expression; | ||
| import software.amazon.awssdk.enhanced.dynamodb.Key; | ||
| import software.amazon.awssdk.services.dynamodb.model.AttributeValue; | ||
| import software.amazon.awssdk.services.dynamodb.model.DeleteItemRequest; | ||
| import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; | ||
| import software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity; | ||
|
|
@@ -289,6 +292,22 @@ public Builder returnValuesOnConditionCheckFailure(String returnValuesOnConditio | |
| return this; | ||
| } | ||
|
|
||
| /** | ||
| * Adds optimistic locking to this delete request. | ||
| * <p> | ||
| * This method applies a condition expression that ensures the delete operation only succeeds | ||
| * if the version attribute of the item matches the provided expected value. | ||
| * | ||
| * @param versionValue the expected version value that must match for the deletion to succeed | ||
| * @param versionAttributeName the name of the version attribute in the DynamoDB table | ||
| * @return a builder of this type with optimistic locking condition applied | ||
| * @throws IllegalArgumentException if any parameter is null | ||
|
||
| */ | ||
| public Builder withOptimisticLocking(AttributeValue versionValue, String versionAttributeName) { | ||
| Expression optimisticLockingCondition = createVersionCondition(versionValue, versionAttributeName); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is it possible that withOptimisticLocking() silently overwrites any existing conditionExpression ? should this use Use Expression.join() to merge with any existing condition instead ?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have updated the code in order to merge the optimistic locking condition with the other existing conditions, if available: /**
* Adds optimistic locking to this delete request.
* <p>
* If a {@link #conditionExpression(Expression)} was already set, this will combine it with the optimistic locking
* condition using {@code AND}. If either expression has conflicting name/value tokens, {@link Expression#join} will throw
* {@link IllegalArgumentException}.
*
* @param versionValue the expected version value that must match for the deletion to succeed
* @param versionAttributeName the name of the version attribute in the DynamoDB table
* @return a builder of this type with optimistic locking condition applied (and merged if needed)
*/
public Builder withOptimisticLocking(AttributeValue versionValue, String versionAttributeName) {
Expression optimisticLockingCondition = createVersionCondition(versionValue, versionAttributeName);
this.conditionExpression = Expression.join(this.conditionExpression, optimisticLockingCondition, " AND ");
return this;
}
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have updated the code in order to merge the optimistic locking condition with the existing ones: public static DeleteItemEnhancedRequest optimisticLocking(
DeleteItemEnhancedRequest.Builder requestBuilder,
AttributeValue versionValue, String versionAttributeName) {
Expression mergedCondition = mergeConditions(
requestBuilder.build().conditionExpression(),
createVersionCondition(versionValue, versionAttributeName));
return requestBuilder
.conditionExpression(mergedCondition)
.build();
}
public static Expression mergeConditions(Expression initialCondition, Expression optimisticLockingCondition) {
return Expression.join(initialCondition, optimisticLockingCondition, " AND ");
}Added tests in OptimisticLockingCrudTest / OptimisticLockingAsyncCrudTest: // 30. deleteItem(DeleteItemEnhancedRequest) with Optimistic Locking true + custom condition respected
// -> deletes the record
@Test
public void deleteItemWithRequest_whenOptimisticLockingHelperMergesConditionAndCustomConditionRespected_deletesTheRecord() {
VersionedRecordWithDeleteOptimisticLocking item =
new VersionedRecordWithDeleteOptimisticLocking().setId("123").setSort(10).setStringAttribute("test");
Key recordKey = Key.builder().partitionValue(item.getId()).sortValue(item.getSort()).build();
versionedRecordWithDeleteLockingTable.putItem(item);
VersionedRecordWithDeleteOptimisticLocking savedItem =
versionedRecordWithDeleteLockingTable.getItem(r -> r.key(recordKey));
Map<String, String> expressionNames = new HashMap<>();
expressionNames.put("#stringAttribute", "stringAttribute");
Map<String, AttributeValue> expressionValues = new HashMap<>();
expressionValues.put(":value", AttributeValue.fromS("test"));
Expression conditionExpression =
Expression.builder()
.expression("#stringAttribute = :value")
.expressionNames(Collections.unmodifiableMap(expressionNames))
.expressionValues(Collections.unmodifiableMap(expressionValues))
.build();
DeleteItemEnhancedRequest.Builder requestBuilder =
DeleteItemEnhancedRequest.builder()
.key(recordKey)
.conditionExpression(conditionExpression);
DeleteItemEnhancedRequest requestWithMergedConditions =
optimisticLocking(requestBuilder,
AttributeValue.builder().n(savedItem.getVersion().toString()).build(),
"version");
versionedRecordWithDeleteLockingTable.deleteItem(requestWithMergedConditions);
VersionedRecordWithDeleteOptimisticLocking deletedItem =
versionedRecordWithDeleteLockingTable.getItem(r -> r.key(recordKey));
assertThat(deletedItem).isNull();
}
// 31. deleteItem(DeleteItemEnhancedRequest) with Optimistic Locking true + custom condition fails
// -> does NOT delete the record
@Test
public void deleteItemWithRequest_whenOptimisticLockingHelperMergesConditionAndCustomConditionFails_doesNotDeleteTheRecord() {
VersionedRecordWithDeleteOptimisticLocking item =
new VersionedRecordWithDeleteOptimisticLocking().setId("123").setSort(10).setStringAttribute("test");
Key recordKey = Key.builder().partitionValue(item.getId()).sortValue(item.getSort()).build();
versionedRecordWithDeleteLockingTable.putItem(item);
VersionedRecordWithDeleteOptimisticLocking savedItem =
versionedRecordWithDeleteLockingTable.getItem(r -> r.key(recordKey));
Map<String, String> expressionNames = new HashMap<>();
expressionNames.put("#stringAttribute", "stringAttribute");
Map<String, AttributeValue> expressionValues = new HashMap<>();
expressionValues.put(":value", AttributeValue.fromS("nonMatchingValue"));
Expression conditionExpression =
Expression.builder()
.expression("#stringAttribute = :value")
.expressionNames(Collections.unmodifiableMap(expressionNames))
.expressionValues(Collections.unmodifiableMap(expressionValues))
.build();
DeleteItemEnhancedRequest.Builder requestBuilder =
DeleteItemEnhancedRequest.builder()
.key(recordKey)
.conditionExpression(conditionExpression);
DeleteItemEnhancedRequest requestWithMergedConditions =
optimisticLocking(requestBuilder,
AttributeValue.builder().n(savedItem.getVersion().toString()).build(),
"version");
assertThatThrownBy(() -> versionedRecordWithDeleteLockingTable.deleteItem(requestWithMergedConditions))
.isInstanceOf(ConditionalCheckFailedException.class)
.satisfies(e -> assertThat(e.getMessage()).contains("The conditional request failed"));
} |
||
| return conditionExpression(optimisticLockingCondition); | ||
| } | ||
|
|
||
| public DeleteItemEnhancedRequest build() { | ||
| return new DeleteItemEnhancedRequest(this); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,145 @@ | ||
| /* | ||
| * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"). | ||
| * You may not use this file except in compliance with the License. | ||
| * A copy of the License is located at | ||
| * | ||
| * http://aws.amazon.com/apache2.0 | ||
| * | ||
| * or in the "license" file accompanying this file. This file is distributed | ||
| * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either | ||
| * express or implied. See the License for the specific language governing | ||
| * permissions and limitations under the License. | ||
| */ | ||
|
|
||
| package software.amazon.awssdk.enhanced.dynamodb.model; | ||
|
|
||
| import java.util.Optional; | ||
| import software.amazon.awssdk.annotations.SdkPublicApi; | ||
| import software.amazon.awssdk.enhanced.dynamodb.Expression; | ||
| import software.amazon.awssdk.enhanced.dynamodb.TableSchema; | ||
| import software.amazon.awssdk.services.dynamodb.model.AttributeValue; | ||
|
|
||
| /** | ||
| * Utility class for adding optimistic locking to DynamoDB delete operations. | ||
| * <p> | ||
| * Optimistic locking prevents concurrent modifications by checking that an item's version hasn't changed since it was last read. | ||
| * If the version has changed, the delete operation fails with a {@code ConditionalCheckFailedException}. | ||
| */ | ||
| @SdkPublicApi | ||
|
||
| public final class OptimisticLockingHelper { | ||
|
|
||
| private OptimisticLockingHelper() { | ||
| } | ||
|
|
||
| /** | ||
| * Adds optimistic locking to a delete request. | ||
| * | ||
| * @param request the original delete request | ||
| * @param versionValue the expected version value | ||
| * @param versionAttributeName the version attribute name | ||
| * @return delete request with optimistic locking condition | ||
| */ | ||
| public static DeleteItemEnhancedRequest withOptimisticLocking( | ||
| DeleteItemEnhancedRequest request, AttributeValue versionValue, String versionAttributeName) { | ||
|
|
||
| Expression conditionExpression = createVersionCondition(versionValue, versionAttributeName); | ||
| return request.toBuilder() | ||
| .conditionExpression(conditionExpression) | ||
| .build(); | ||
| } | ||
|
|
||
| /** | ||
| * Adds optimistic locking to a transactional delete request. | ||
| * | ||
| * @param request the original transactional delete request | ||
| * @param versionValue the expected version value | ||
| * @param versionAttributeName the version attribute name | ||
| * @return transactional delete request with optimistic locking condition | ||
| */ | ||
| public static TransactDeleteItemEnhancedRequest withOptimisticLocking( | ||
| TransactDeleteItemEnhancedRequest request, AttributeValue versionValue, String versionAttributeName) { | ||
|
|
||
| Expression conditionExpression = createVersionCondition(versionValue, versionAttributeName); | ||
| return request.toBuilder() | ||
| .conditionExpression(conditionExpression) | ||
| .build(); | ||
| } | ||
|
|
||
| /** | ||
| * Conditionally applies optimistic locking if enabled and version information exists. | ||
| * | ||
| * @param <T> the type of the item | ||
| * @param request the original delete request | ||
| * @param keyItem the item containing version information | ||
| * @param tableSchema the table schema | ||
| * @param useOptimisticLocking if true, applies optimistic locking | ||
| * @return delete request with optimistic locking if enabled and version exists, otherwise original request | ||
| */ | ||
| public static <T> DeleteItemEnhancedRequest conditionallyApplyOptimisticLocking( | ||
| DeleteItemEnhancedRequest request, T keyItem, TableSchema<T> tableSchema, boolean useOptimisticLocking) { | ||
|
|
||
| if (!useOptimisticLocking) { | ||
| return request; | ||
| } | ||
|
|
||
| return getVersionAttributeName(tableSchema) | ||
| .map(versionAttributeName -> { | ||
| AttributeValue version = tableSchema.attributeValue(keyItem, versionAttributeName); | ||
| return version != null ? withOptimisticLocking(request, version, versionAttributeName) : request; | ||
| }) | ||
| .orElse(request); | ||
| } | ||
|
|
||
| /** | ||
| * Conditionally applies optimistic locking if enabled and version information exists. | ||
| * | ||
| * @param <T> the type of the item | ||
| * @param request the original transactional delete request | ||
| * @param keyItem the item containing version information | ||
| * @param tableSchema the table schema | ||
| * @param useOptimisticLocking if true, applies optimistic locking | ||
| * @return delete request with optimistic locking if enabled and version exists, otherwise original request | ||
| */ | ||
| public static <T> TransactDeleteItemEnhancedRequest conditionallyApplyOptimisticLocking( | ||
| TransactDeleteItemEnhancedRequest request, T keyItem, TableSchema<T> tableSchema, boolean useOptimisticLocking) { | ||
|
|
||
| if (!useOptimisticLocking) { | ||
| return request; | ||
| } | ||
|
|
||
| return getVersionAttributeName(tableSchema) | ||
| .map(versionAttributeName -> { | ||
| AttributeValue version = tableSchema.attributeValue(keyItem, versionAttributeName); | ||
| return version != null ? withOptimisticLocking(request, version, versionAttributeName) : request; | ||
| }) | ||
| .orElse(request); | ||
| } | ||
|
|
||
|
|
||
| /** | ||
| * Creates a version condition expression. | ||
| * | ||
| * @param versionValue the expected version value | ||
| * @param versionAttributeName the version attribute name | ||
| * @return version check condition expression | ||
| */ | ||
| public static Expression createVersionCondition(AttributeValue versionValue, String versionAttributeName) { | ||
| return Expression.builder() | ||
| .expression(versionAttributeName + " = :version_value") | ||
|
||
| .putExpressionValue(":version_value", versionValue) | ||
| .build(); | ||
| } | ||
|
|
||
| /** | ||
| * Gets the version attribute name from table schema. | ||
| * | ||
| * @param <T> the type of the item | ||
| * @param tableSchema the table schema | ||
| * @return version attribute name if present, empty otherwise | ||
| */ | ||
| public static <T> Optional<String> getVersionAttributeName(TableSchema<T> tableSchema) { | ||
| return tableSchema.tableMetadata().customMetadataObject("VersionedRecordExtension:VersionAttribute", String.class); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,6 +15,8 @@ | |
|
|
||
| package software.amazon.awssdk.enhanced.dynamodb.model; | ||
|
|
||
| import static software.amazon.awssdk.enhanced.dynamodb.model.OptimisticLockingHelper.createVersionCondition; | ||
|
|
||
| import java.util.Objects; | ||
| import java.util.function.Consumer; | ||
| import software.amazon.awssdk.annotations.NotThreadSafe; | ||
|
|
@@ -24,6 +26,7 @@ | |
| import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient; | ||
| import software.amazon.awssdk.enhanced.dynamodb.Expression; | ||
| import software.amazon.awssdk.enhanced.dynamodb.Key; | ||
| import software.amazon.awssdk.services.dynamodb.model.AttributeValue; | ||
| import software.amazon.awssdk.services.dynamodb.model.ReturnValuesOnConditionCheckFailure; | ||
|
|
||
| /** | ||
|
|
@@ -215,6 +218,21 @@ public Builder returnValuesOnConditionCheckFailure(String returnValuesOnConditio | |
| return this; | ||
| } | ||
|
|
||
| /** | ||
| * Adds optimistic locking to this transactional delete request. | ||
| * <p> | ||
| * This method applies a condition expression that ensures the delete operation only succeeds if the version attribute of | ||
| * the item matches the provided expected value. If the condition fails, the entire transaction will be cancelled. | ||
| * | ||
| * @param versionValue the expected version value that must match for the deletion to succeed | ||
| * @param versionAttributeName the name of the version attribute in the DynamoDB table | ||
| * @return a builder of this type with optimistic locking condition applied | ||
| * @throws IllegalArgumentException if any parameter is null | ||
|
||
| */ | ||
| public Builder withOptimisticLocking(AttributeValue versionValue, String versionAttributeName) { | ||
| Expression optimisticLockingCondition = createVersionCondition(versionValue, versionAttributeName); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is it possible that withOptimisticLocking() silently overwrites any existing conditionExpression ? should this use Use Expression.join() to merge with any existing condition instead ?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have updated the code in order to merge the optimistic locking condition with the other existing conditions, if available: /**
* Adds optimistic locking to this delete request.
* <p>
* If a {@link #conditionExpression(Expression)} was already set, this will combine it with the optimistic locking
* condition using {@code AND}. If either expression has conflicting name/value tokens, {@link Expression#join} will throw
* {@link IllegalArgumentException}.
*
* @param versionValue the expected version value that must match for the deletion to succeed
* @param versionAttributeName the name of the version attribute in the DynamoDB table
* @return a builder of this type with optimistic locking condition applied (and merged if needed)
*/
public Builder withOptimisticLocking(AttributeValue versionValue, String versionAttributeName) {
Expression optimisticLockingCondition = createVersionCondition(versionValue, versionAttributeName);
this.conditionExpression = Expression.join(this.conditionExpression, optimisticLockingCondition, " AND ");
return this;
} |
||
| return conditionExpression(optimisticLockingCondition); | ||
| } | ||
|
|
||
| public TransactDeleteItemEnhancedRequest build() { | ||
| return new TransactDeleteItemEnhancedRequest(this); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing Javadoc ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added JavaDoc for this method.