-
Notifications
You must be signed in to change notification settings - Fork 937
Description
Describe the bug
Hello, I am running DynamoDbLocal version 3.0.0. I am creating a table in a @SpringBootTest
like this:
@TestInstance(TestInstance.Lifecycle.PER_CLASS) // Needed since the beforeAll method needs to be non-static
@SpringBootTest
class DefaultItemServiceTests {
@BeforeAll
void beforeAll() throws Exception {
String port = "8000"
server = ServerRunner.createServerFromCommandLineArgs(
new String[]{"-inMemory", "-port", port})
server.start()
dbClient = DynamoDbClient.builder().endpointOverride(new URI("http://localhost:8000")).build()
dbClient.createTable(CreateTableRequest.builder()
.tableName("items")
.keySchema(List.of(KeySchemaElement.builder().keyType(KeyType.HASH).attributeName("username").build(),
KeySchemaElement.builder().keyType(KeyType.RANGE).attributeName("itemId").build()) as Collection<KeySchemaElement>)
.globalSecondaryIndexes(GlobalSecondaryIndex.builder().keySchema(
List.of(KeySchemaElement.builder().keyType(KeyType.HASH).attributeName("secondaryPartitionKey").build(),
KeySchemaElement.builder().keyType(KeyType.RANGE).attributeName("itemId").build()) as Collection<KeySchemaElement>
).indexName("items-global-chronological-index").projection(Projection.builder().projectionType(ProjectionType.ALL).build()).build()
).attributeDefinitions(
List.of(AttributeDefinition.builder().attributeName("username").attributeType(ScalarAttributeType.S).build(),
AttributeDefinition.builder().attributeName("itemId").attributeType(ScalarAttributeType.S).build(),
AttributeDefinition.builder().attributeName("secondaryPartitionKey").attributeType(ScalarAttributeType.S).build()
)
).billingMode(BillingMode.PAY_PER_REQUEST)
.build() as CreateTableRequest
}
}
Regression Issue
- Select this option if this issue appears to be a regression.
Expected Behavior
I expected the DynamoDB Enhanced Client to send a Query request like the following:
2025-08-27 18:17:58,098 [main] DEBUG org.apache.http.wire - http-outgoing-3 >>
"{"TableName":"items","IndexName":"items-global-chronological-index",
"KeyConditionExpression":"#AMZN_MAPPED_secondaryPrimaryKey = :AMZN_MAPPED_secondaryPrimaryKey","ExpressionAttributeNames":{"#AMZN_MAPPED_secondaryPrimaryKey":"secondaryPrimaryKey"},"ExpressionAttributeValues":{":AMZN_MAPPED_secondaryPrimaryKey":{"S":"1"}}}"
Current Behavior
The table appears to be created correctly according to the wire logs:
2025-08-27 18:17:57,638 [main] DEBUG org.apache.http.wire - http-outgoing-2 >> "{"AttributeDefinitions":[{"AttributeName":"username","AttributeType":"S"},{"AttributeName":"itemId","AttributeType":"S"},{"AttributeName":"secondaryPartitionKey","AttributeType":"S"}],"TableName":"items","KeySchema":[{"AttributeName":"username","KeyType":"HASH"},{"AttributeName":"itemId","KeyType":"RANGE"}],"GlobalSecondaryIndexes":[{"IndexName":"items-global-chronological-index","KeySchema":[{"AttributeName":"secondaryPartitionKey","KeyType":"HASH"},{"AttributeName":"itemId","KeyType":"RANGE"}],"Projection":{"ProjectionType":"ALL"}}],"BillingMode":"PAY_PER_REQUEST"}"
However, the DynamoDB Enhanced Client will send a Query request like the following:
2025-08-27 18:17:58,098 [main] DEBUG org.apache.http.wire - http-outgoing-3 >>
"{"TableName":"items","IndexName":"items-global-chronological-index",
"KeyConditionExpression":"#AMZN_MAPPED_username = :AMZN_MAPPED_username","ExpressionAttributeNames":{"#AMZN_MAPPED_username":"username"},"ExpressionAttributeValues":{":AMZN_MAPPED_username":{"S":"1"}}}"
This query incorrectly references the primary Partition Key instead of the Partition key I configured for that index
Reproduction Steps
Use this model class:
@EqualsAndHashCode
@ToString
@NoArgsConstructor
@DynamoDbBean
class Item {
String username
String itemId
String creationTimestamp
String lastUpdatedTimestamp
@DynamoDbPartitionKey
String getUsername() {
username
}
@DynamoDbSortKey
@DynamoDbSecondarySortKey(indexNames = [ "items-global-chronological-index"])
String getItemId() {
itemId
}
@DynamoDbSecondaryPartitionKey(indexNames = [ "items-global-chronological-index" ])
String getSecondaryPartitionKey() {
"1"
}
String getCreationTimestamp() {
creationTimestamp
}
String getLastUpdatedTimestamp() {
lastUpdatedTimestamp
}
}
Run the code above to create the table in a Spring Boot Test. Use the following configuration class:
@Configuration
class TestDynamoDbConfig {
@Value('${app.config.dynamodb.items.table}')
private String dynamoDbTableName
@Bean
@Primary
DynamoDbTable<Item> testDynamoDbTable() {
DynamoDbEnhancedClient.builder().dynamoDbClient(DynamoDbClient.builder().credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create("key", "secret"))).endpointOverride(new URI("http://localhost:8000")).build())
.build().table(dynamoDbTableName, TableSchema.fromBean(Item.class))
}
}
And run the following by calling from the main method or something:
@Component
class DefaultItemService {
private DynamoDbTable<Item> table
private final String gsiName
DefaultItemService(DynamoDbTable<Item> dynamoDbTable, @Value('${app.config.dynamodb.item.secondaryIndex.name}') String secondaryIndex) {
this.table = dynamoDbTable
this.gsiName = secondaryIndex
}
void query() {
DynamoDbIndex<Item> index = table.index(gsiName)
QueryConditional queryConditional = QueryConditional.keyEqualTo(Key.builder().partitionValue("1").build())
QueryEnhancedRequest queryEnhancedRequest = QueryEnhancedRequest.builder().queryConditional(queryConditional).build()
Item item = index.query(queryEnhancedRequest)
.first()
.items()
.first()
}
}
The end result of this should be an error like this:
software.amazon.awssdk.services.dynamodb.model.DynamoDbException: Query condition missed key schema element
Possible Solution
No response
Additional Information/Context
No response
AWS Java SDK version used
2.32.31
JDK version used
openjdk version "17.0.16" 2025-07-15. Groovy 3.0.20
Operating System and version
Linux Mint 21.3 x86_64