aws / aws/aws-sdk-java-v2

AutoGenerateUUIDExtension Overriding Existing Attribute Values

Open
#5,703 5 comments 1 reaction 0 assignees View on GitHub
bug dynamodb-enhanced p2
Dominant language
Java
Stars
2.6k
Forks
1k
Avg merge
2d 9h
Merged PRs (30d)
51

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:

```java
@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:

```java
@Configuration
public class DynamoDbConfig {
@Bean
public DynamoDbTable 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` using the `DynamoDbEnhancedClient`:

```java
@Service
public class DynamoDbTableService {

@Autowired
private DynamoDbEnhancedClient dynamoDbEnhancedClient;

public DynamoDbTable getDynamoDbTable() {
return dynamoDbEnhancedClient.table("MyEntityTable", TableSchema.fromBean(MyEntity.class));
}
}
```

### 4. Create a controller endpoints for table creation and record saving/updating
```java
@RestController
public class ApiController {

@Autowired
DynamoDbTable 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
```json
{
"customerName": "XXYYZZ"
}
```

After record creation you will receive a save records copy as response
```json
{
"customerId": "623bc3df-579f-4b3d-841e-bed00cfeea0f",
"customerName": "XXYYZZ", //modify to send as body again
"createdDate": "2024-11-09T08: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
1. **Create** a new `MyEntity` object, save it using `updateItem()`.
2. **Modify** the object's attributes (except for the partition key).
3. 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 `@DynamoDbAutoGeneratedUuid` annotation 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

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.