DynamoDB and TableNameOverride with prefix

I've faced the same situation and struggled with myself a couple of days to get that working.

Just in case you're using DynamoDB + Spring here is what worked for me:

POJO class:

@DynamoDBTable(tableName = "APP-ACCESSKEY")
public class AccessKey {
    @NotBlank
    @Size(min = 1, max = 36)
    private String accessToken;

    @NotNull
    @Size(min = 3, max = 15)
    private String userName;

    private Date dateInsertion;


    public AccessKey() {

    // ... All POJO stuff
}

Spring configuration:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">

    <!-- Amazon Credentials -->
    <bean id="tableNameOverride" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <property name="staticMethod" value="com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperConfig.TableNameOverride.withTableNamePrefix"/>
        <property name="arguments" value="DES-" />
    </bean>

    <bean id="dynamoDBMapperConfig" class="com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperConfig">
        <constructor-arg index="0" ref="tableNameOverride" />
    </bean>

    <bean id="BasicAWSCredentials" class="com.amazonaws.auth.BasicAWSCredentials">
         <constructor-arg index="0" value="${amazon.accessKey}" />
         <constructor-arg index="1" value="${amazon.secretKey}" />
    </bean>

    <bean id="amazonDynamoDBClient" class="com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient">
        <constructor-arg index="0" ref="BasicAWSCredentials" />
        <property name="endpoint" value="http://dynamodb.us-west-2.amazonaws.com" />
    </bean>

    <bean id="dynamoDBMapper" class="com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper">
        <constructor-arg index="0" ref="amazonDynamoDBClient" />
        <constructor-arg index="1" ref="dynamoDBMapperConfig" />
    </bean>

</beans>

Explanation:

Taking into account that my AccessKey object point to APP-ACCESSKEY table on AWS DynamodDB then it turns out that after running this, your application will start to point to DES-APP-ACCESSKEY.

Hope it helps someone who's facing a situation akin to it

Cheers


Same as Paolo Almeidas solution, but with Spring-Boot annotations. Just wanted to share it and maybe save someone time:

I have dynamodb tables for each namespace, e.g. myApp-dev-UserTable, myApp-prod-UserTable and I am using the EKS_NAMESPACE env variable, which in my case gets injected into the pods by kubernetes.

import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperConfig;

@Configuration
@EnableDynamoDBRepositories(basePackages = "de.dynamodb")
public class DynamoDBConfig {

    @Value("${EKS_NAMESPACE}")
    String eksNamespace;

    @Bean
    public AmazonDynamoDB amazonDynamoDB() {
        return AmazonDynamoDBClientBuilder.standard()
            .withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(
                    "dynamodb.eu-central-1.amazonaws.com", "eu-central-1"))
            .withCredentials(awsCredentials())
            .build();
    }

    @Bean
    public AWSCredentialsProvider awsCredentials() {
        return WebIdentityTokenCredentialsProvider.builder().build();
    }

    // Table Name override:

    @Bean
    public DynamoDBMapperConfig.TableNameOverride tableNameOverride() {
        return DynamoDBMapperConfig.TableNameOverride.withTableNamePrefix("myApp-" + eksNamespace + "-");
    }

    @Bean
    public DynamoDBMapperConfig dynamoDBMapperConfig() {
        return DynamoDBMapperConfig.builder().withTableNameOverride(tableNameOverride()).build();
    }

    @Bean
    // Marked as primary bean to override default bean.
    @Primary
    public DynamoDBMapper dynamoDBMapper() {
        return new DynamoDBMapper(amazonDynamoDB(), dynamoDBMapperConfig());
    }
}

With a table like this:

@Data
@DynamoDBTable(tableName = "UserTable")
public class User {

        @DynamoDBHashKey
        private String userId;

        @DynamoDBAttribute
        private String foo;

        @DynamoDBAttribute
        private String bar;
}

withTableNamePrefix is a static method. So this line is creating a new instance of TableNameOverride with the String "test", and then throwing that instance away by using it to call the static withTableNamePrefix method:

TableNameOverride tbl = new TableNameOverride("test").withTableNamePrefix("dev_");

To answer the deeper question of separating test from prod, I would recommend having 2 separate AWS Accounts entirely, one for dev and one for prod. This is the only way you can:

  • See billing separately
  • Ensure you never leak data between prod and test systems
  • Have high scaling on a dev table prevent you from scaling a prod table higher