snakespan
snakespan

Reputation: 1211

ValidationException: ExpressionAttributeNames can only be specified when using expressions using DynamoDB Put

I'm trying to put an item in a DynamoDB table. I'm basically swapping usage of update item with put item which has the exact same logic as below, but for put item, I am getting errors:

ValidationException: ExpressionAttributeNames can only be specified when using expressions

Below is the code:

// Puts/updates item into DynamoDB table
public boolean setSessionString(String sessionId, String clientDomain, Map<String, String> keyValueMap,
                                long ttl) {

    // TTL since epoch calculation
    long timeToLiveInSeconds = ttl == 0 ? DEFAULT_TTL : ttl;
    long expirationTime = System.currentTimeMillis() / 1000L + timeToLiveInSeconds;

    try {
        // Construct putItemSpec
        PutItemSpec putItemSpec = createPutItemSpec(
                createPrimaryKey(sessionId, clientDomain), keyValueMap, expirationTime);

        table.putItem(putItemSpec);
        return true;

    } catch (final Exception e) {
        throw DependencyException.builder()
              .withMessage("DynamoDB update/put item error: " + e.getMessage())
              .withCause(e.getCause())
              .build();
    }
}

// Enumerate key-value pairs to escape DynamoDB reserved keyword
private PutItemSpec createPutItemSpec(PrimaryKey primaryKey, Map<String, String> keyValueMap, long expirationTime) {
    Item item = new Item().withPrimaryKey(primaryKey);
    PutItemSpec putItemSpec = new PutItemSpec();

    List<String> keyList = new ArrayList<String>(keyValueMap.keySet());
    NameMap nameMap = new NameMap();
    ValueMap valueMap = new ValueMap();

    // Enumerating key-value entries
    for (int keyValueIndex = 0; keyValueIndex < keyList.size(); keyValueIndex++) {
        String keyEnum = "#k" + keyValueIndex;
        String valueEnum = ":v" + keyValueIndex;
        String key = keyList.get(keyValueIndex);
        nameMap.with(keyEnum, key);
        valueMap.with(valueEnum, keyValueMap.get(key));
    }

    // Appending ttl attribute
    String ttlAttributeEscape = "#" + RESERVED_TTL_ATTRIBUTE;
    String ttlValueEscape = ":" + RESERVED_TTL_ATTRIBUTE;
    nameMap.with(ttlAttributeEscape, RESERVED_TTL_ATTRIBUTE);
    valueMap.withLong(ttlValueEscape, expirationTime);

    return putItemSpec.withNameMap(nameMap)
                      .withValueMap(valueMap)
                      .withItem(item);
}

Any suggestions on what I might be missing? There could be a key difference between update and put item spec perhaps.

Upvotes: 1

Views: 1564

Answers (1)

smac2020
smac2020

Reputation: 10744

You are not using the latest Amazon DynamoDB API to put an item into an Amazon DynamoDB table. The recommended way is to use the AWS SDK for Java V2 and the Enchanced Client. Using the Enhanced Client, you can simply create a Java POJO, use its methods to set the values, and then simply call the putItem method and pass the Java object. (You do not need to use LISTS, etc)

Docs here:

Mapping items in DynamoDB tables

Here is a Java V2 example:

package com.example.dynamodb;

// snippet-start:[dynamodb.java2.mapping.putitem.import]
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable;
import software.amazon.awssdk.enhanced.dynamodb.TableSchema;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSortKey;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
// snippet-end:[dynamodb.java2.mapping.putitem.import]

/*
 * Prior to running this code example, create an Amazon DynamoDB table named Customer with these columns:
 *   - id - the id of the record that is the key
 *   - custName - the customer name
 *   - email - the email value
 *   - registrationDate - an instant value when the item was added to the table
 *
 * Also, ensure that you have setup your development environment, including your credentials.
 *
 * For information, see this documentation topic:
 *
 * https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html
 */
public class EnhancedPutItem {

    public static void main(String[] args) {

        Region region = Region.US_EAST_1;
        DynamoDbClient ddb = DynamoDbClient.builder()
                .region(region)
                .build();

        DynamoDbEnhancedClient enhancedClient = DynamoDbEnhancedClient.builder()
                .dynamoDbClient(ddb)
                .build();

        putRecord(enhancedClient) ;
        ddb.close();
    }

    // snippet-start:[dynamodb.java2.mapping.putitem.main]
    // Puts an item into a DynamoDB table
    public static void putRecord(DynamoDbEnhancedClient enhancedClient) {

        try {
            DynamoDbTable<Customer> custTable = enhancedClient.table("Customer", TableSchema.fromBean(Customer.class));

            // Create an Instant
            LocalDate localDate = LocalDate.parse("2020-04-07");
            LocalDateTime localDateTime = localDate.atStartOfDay();
            Instant instant = localDateTime.toInstant(ZoneOffset.UTC);

            // Populate the Table
            Customer custRecord = new Customer();
            custRecord.setCustName("Susan red");
            custRecord.setId("id140");
            custRecord.setEmail("[email protected]");
            custRecord.setRegistrationDate(instant) ;

            // Put the customer data into a DynamoDB table
            custTable.putItem(custRecord);

        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }
        System.out.println("done");
    }
    // snippet-end:[dynamodb.java2.mapping.putitem.main]
}

Here is the corresponding Customer Class;

package com.example.dynamodb;

import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable;
import software.amazon.awssdk.enhanced.dynamodb.TableSchema;
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.DynamoDbSortKey;
import software.amazon.awssdk.enhanced.dynamodb.model.BatchWriteItemEnhancedRequest;
import software.amazon.awssdk.enhanced.dynamodb.model.WriteBatch;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneOffset;

/**
 * This class is used by the Enhanced Client examples.
 */

 @DynamoDbBean
 public class Customer {

        private String id;
        private String name;
        private String email;
        private Instant regDate;

        @DynamoDbPartitionKey
        public String getId() {
            return this.id;
        };

        public void setId(String id) {

            this.id = id;
        }

        public String getCustName() {
            return this.name;
        }

        public void setCustName(String name) {
            this.name = name;
        }

        public String getEmail() {
            return this.email;
        }

        public void setEmail(String email) {
            this.email = email;
        }

        public Instant getRegistrationDate() {
            return regDate;
        }
        public void setRegistrationDate(Instant registrationDate) {

            this.regDate = registrationDate;
        }
    }

You can find additional Enhanced Client examples here.

In addition, here is an AWS tutorial you will find in the AWS SDK Github repo that shows use of this API within a dynamic web app developed by using Spring BOOT.

Creating the DynamoDB web application item tracker

There resources show you the way to use the latest Amazon DynamoDB API.

Upvotes: 2

Related Questions