Wednesday, September 14, 2022

DynamoDB query from Global Secondary Index with AWS 2.X

With AWS 2.X enhanced DynamoDB client, they have redesigned ‘DynamoDB mapper’ in the Java v1 SDK. In latest version, it requires the annotation @DynamoDbBean in POJO that identifies the class as being a DynamoDb mappable entity. Let's look at how to query Global Secondary Index(GSI).

Solution
Example POJO using DynamoDbBean:
       

    import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean;
    import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey;
    import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSecondaryPartitionKey;

    @DynamoDbBean
    public class Customer {

        private String identifier;
        private String name;
        private int age;


        @DynamoDbPartitionKey
        public String getIdentifier(){
            return this.identifier;
        }
        public void setIdentifier(String transactionID){
            this.identifier  = transactionID;
        }

        @DynamoDbSecondaryPartitionKey(indexNames = {"name-index"}) 
        public String getName(){
            return name;
        }
        public void setName(String name){
            this.name = name;
        }

        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }

    }

  

       
 
following is how you can query GSI using enhanced DynamoDB client.
       

	public void getCustomersByName(String name) {
		
		DynamoDbEnhancedClient enhancedClient = DynamoDbEnhancedClient.builder()
		        .dynamoDbClient(ddb)
		        .build();
		
		DynamoDbTable customerTable = enhancedClient.table("Customer", TableSchema.fromBean(Customer.class));
		
		
		DynamoDbIndex secIndex = customerTable.index("name-index");
        AttributeValue attVal = AttributeValue.builder().s(name).build();
        QueryConditional queryConditional = QueryConditional
                .keyEqualTo(Key.builder().partitionValue(attVal).build());

        Iterable> results =  secIndex.query(
                QueryEnhancedRequest.builder()
                        .queryConditional(queryConditional)
                        .build());
        
        results.forEach(page -> {
            List customers = page.items();
            for (Customer c: customers) {
                System.out.println(c.getName());
            }
        });
		
	}

       
       

 

Wednesday, July 27, 2022

Using dynamic table name when saving item to DynamDB Table

If you are using Java DynamoDBMapper API, it requires the annotation @DynamoDBTable on the POJO. This is a compile-time annotation and requires a table name parameter. How to change/pass this table name in runtime?

Solution
Looking a bit closer at the DynamoMapper API, I found a class called DynamoDBMapperConfig. This class has a builder method which allows us to specify a so called TableNameOverride that overrides the table name defined in the DynamoDBTable annotation in Java class. For example if you want to override the table name defined in the following class;
       

    @DynamoDBTable(tableName = "temp_name")
    public static class IDRecord {
        private String id;
        private String StartDate;
        private String EndDate;

        @DynamoDBHashKey(attributeName = "ID")
        public String geID() {
            return id;
        }

        public void setID(String id) {
            this.id = id;
        }

        @DynamoDBAttribute(attributeName = "StartDate")
        public String getStartDate() {
            return StartDate;
        }

        public void setStartDate(String StartDate) {
            this.StartDate = StartDate;
        }

        @DynamoDBAttribute(attributeName = "EndDate")
        public String getEndDate() {
            return EndDate;
        }

        public void setEndDate(String EndDate) {
            this.EndDate = EndDate;
        }

        @Override
        public String toString() {
            return "IDRecord [ID=" + id + ", StartDate=" + StartDate  + ", EndDate=" + EndDate+ "]";
        }

    }
  

       
 
when querying the table, do the following. This will ignore the table name defined by the @DynamoDBTable annotation in IDRecord class and instead use the tableName that we read from environment variable.
       

private static AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().withRegion("ap-southeast-2").build();
	
private static DynamoDBMapper dynamoDBMapper = new DynamoDBMapper(client);
    
DynamoDBMapperConfig mapperConfig = DynamoDBMapperConfig.builder().withTableNameOverride(new TableNameOverride(tableName)).build();
                
dynamoDBMapper.save(iDRecord, mapperConfig);

       
       
Note: In previous version, it had DynamoDBMapperConfig(DynamoDBMapperConfig.TableNameOverride tableNameOverride) which is now Deprecated