-
Couldn't load subscription status.
- Fork 947
Description
Describe the bug
The AutoGenerateUUIDExtension was incorrectly overriding existing UUID values for items that already contained non-empty values.
This caused some major issues while I working on a project by using DynamoDbTable.updateItem() method. This method didn't update the existing record instead it saves a new record with auto-generated values. When I debuged the code I found the issue is nit with updateItem() method but with the extension. When I tag partition key attribute with tag @DynamoDbAutoGeneratedUuid. I will never be able to update existing record in the Db using updateItem() method.
Regression Issue
- Select this option if this issue appears to be a regression.
Expected Behavior
When provided an an object of DynamoDbBean.
Expected: when you try to update an existing record on the DB. it updates the record as per the attributes provided by the user operation.
Current Behavior
Current: Instead of updating the existing record, the operation saves a new record onto the DB
Reproduction Steps
DynamoDB Bug Replication Steps: Auto-Generated UUID Overwriting Issue
Prerequisites
- Spring Boot project with:
- Lombok library for model annotations.
- AWS SDK v2.29.1 for DynamoDB.
- Local DynamoDB instance set up using NoSQL Workbench.
Steps to Reproduce the Issue
1. Create a DynamoDB Entity (Model)
Create a DynamoDB bean using Lombok annotations:
@DynamoDbBean @NoArgsConstructor @AllArgsConstructor public class Customer { private String customerId; private String customerName; private Instant createdDate; @DynamoDbAttribute(value = "CustomerId") @DynamoDbAutoGeneratedUuid @DynamoDbPartitionKey @DynamoDbUpdateBehavior(UpdateBehavior.WRITE_IF_NOT_EXISTS) public String getCustomerId() { return customerId; } public void setCustomerId(String customerId) { this.customerId = customerId; } @DynamoDbAttribute(value = "CustomerName") public String getCustomerName() { return customerName; } public void setCustomerName(String customerName) { this.customerName = customerName; } @DynamoDbAttribute(value = "CreatedDate") @DynamoDbAutoGeneratedTimestampAttribute @DynamoDbConvertedBy(CstTimeFormatConverter.class) @DynamoDbUpdateBehavior(UpdateBehavior.WRITE_IF_NOT_EXISTS) public Instant getCreatedDate() { return createdDate; } public void setCreatedDate(Instant createdDate) { this.createdDate = createdDate; } }
2. Configure DynamoDB Enhanced Client Bean
Create a DynamoDbEnhancedClient bean in your Spring Boot configuration:
@Configuration public class DynamoDbConfig { @Bean public DynamoDbTable<Customer> customerDynamoDbTable(DynamoDbEnhancedClient dynamoDbEnhancedClient){ return dynamoDbEnhancedClient.table("Customer", TableSchema.fromBean(Customer.class)); } @Bean public DynamoDbClient dynamoDbClient() { return DynamoDbClient.builder() .endpointOverride(java.net.URI.create("http://localhost:8000")) .region(Region.US_EAST_1) // Replace with your desired region .credentialsProvider(StaticCredentialsProvider.create( AwsBasicCredentials.create("dummy", "vyh65"))) // Use dummy credentials for local .build(); } @Bean public DynamoDbEnhancedClient dynamoDbEnhancedClient(DynamoDbClient dynamoDbClient) { return DynamoDbEnhancedClient.builder() .dynamoDbClient(dynamoDbClient) .extensions(AutoGeneratedTimestampRecordExtension.create(), AutoGeneratedUuidExtension.create()) .build(); } }
3. Create a DynamoDB Table Bean
Create a DynamoDbTable<MyEntity> using the DynamoDbEnhancedClient:
@Service public class DynamoDbTableService { @Autowired private DynamoDbEnhancedClient dynamoDbEnhancedClient; public DynamoDbTable<MyEntity> getDynamoDbTable() { return dynamoDbEnhancedClient.table("MyEntityTable", TableSchema.fromBean(MyEntity.class)); } }
4. Create a controller endpoints for table creation and record saving/updating
@RestController public class ApiController { @Autowired DynamoDbTable<Customer> customerDynamoDbTable; @PostMapping("createRecord") public Customer createRecord(@RequestBody Customer customer){ customer = customerDynamoDbTable.updateItem(customer); // Return the updated item return customer; } @PostMapping("createTable") public void createTable(){ customerDynamoDbTable.createTable(); }
6. Attempt creating table using the above API and then save a record by providing body
{
"customerName": "XXYYZZ"
}After record creation you will receive a save records copy as response
{
"customerId": "623bc3df-579f-4b3d-841e-bed00cfeea0f",
"customerName": "XXYYZZ", //modify to send as body again
"createdDate": "2024年11月09日T08:09:45.479307200Z"
}using the modified response as body hit the /createRecord again it should updated the existing record. Instead it creates a new record.
6. Attempt to Update an Existing Record
- Create a new
MyEntityobject, save it usingupdateItem(). - Modify the object's attributes (except for the partition key).
- Use
updateItem()again to update the record in DynamoDB.
Expected Behavior
- The
updateItem()method should update the existing record with new attribute values.
Actual Behavior
- Bug: The
@DynamoDbAutoGeneratedUuidannotation overrides the existing partition key. - The
updateItem()method creates a new record instead of updating the existing one.
Summary
The issue is caused by the @DynamoDbAutoGeneratedUuid tag which regenerates the UUID during updateItem() calls, leading to unintended new record creation instead of updating existing entries.
Possible Solution
No response
Additional Information/Context
No response
AWS Java SDK version used
2.29.1
JDK version used
21
Operating System and version
windows 11 23H2