spring-data-dynamodb icon indicating copy to clipboard operation
spring-data-dynamodb copied to clipboard

TableNameOverride Prefix not applying

Open naumannt opened this issue 6 years ago • 8 comments

Expected Behavior I am using the following relevant classes, built from multiple examples for running a local testing version of DynamoDB to get more familiar with the framework:

General configuration

    @Configuration
    @EnableDynamoDBRepositories(
        dynamoDBMapperConfigRef = "dynamoDBMapperConfig",
        basePackages = "my.model.package") 
    public class DynamoDBConfig {

    private static final Logger log = LoggerFactory.getLogger(DynamoDBConfig.class);

    @Value("${amazon.aws.accesskey}")
    private String amazonAWSAccessKey;

    @Value("${amazon.aws.secretkey}")
    private String amazonAWSSecretKey;

    @Value("${amazon.dynamodb.tableNameOverride}")
    private String tablePrefix;

    public AWSCredentialsProvider amazonAWSCredentialsProvider() {
        return new AWSStaticCredentialsProvider(amazonAWSCredentials());
    }

    @Bean
    public AWSCredentials amazonAWSCredentials() {
        return new BasicAWSCredentials(amazonAWSAccessKey, amazonAWSSecretKey);
    }


    @Bean
    public DynamoDBMapperConfig dynamoDBMapperConfig() {
        // Create empty DynamoDBMapperConfig builder
        DynamoDBMapperConfig.Builder builder = DynamoDBMapperConfig.builder();
        builder.setTableNameOverride(DynamoDBMapperConfig.TableNameOverride.withTableNamePrefix(tablePrefix));

        return new DynamoDBMapperConfig(DynamoDBMapperConfig.DEFAULT, builder.build());
    }

    @Bean
    public DynamoDBMapper dynamoDBMapper(AmazonDynamoDB amazonDynamoDB, DynamoDBMapperConfig config) {
        return new DynamoDBMapper(amazonDynamoDB, config);
    }

    @Bean
    public AmazonDynamoDB amazonDynamoDB() {
        return AmazonDynamoDBClientBuilder.standard()
                .withCredentials(amazonAWSCredentialsProvider())
                .withEndpointConfiguration(
                        new AwsClientBuilder.EndpointConfiguration("http://localhost:8000", "eu-central-1")
                )
                .build();
    }

    @Bean
    public DynamoDBMappingContext dynamoDBMappingContext() {
        return new DynamoDBMappingContext();
    }
`

Class that is run after successful application start to setup database "schema"

    @Service
    public class DBSetupRunner {

    final Logger log = LoggerFactory.getLogger(DBSetupRunner.class);

    @Value("${amazon.dynamodb.tableNameOverride}")
    private String tablePrefix;

    @EventListener(ApplicationReadyEvent.class)
    public void main() throws Exception {
        AmazonDynamoDB dynamodb = null;
        System.setProperty("sqlite4java.library.path", "native-libs");

        // Create an in-memory and in-process instance of DynamoDB Local that runs over HTTP
        final String[] localArgs = { "-inMemory" };
        DynamoDBProxyServer server = null;
            server = ServerRunner.createServerFromCommandLineArgs(localArgs);
            server.start();

            dynamodb = AmazonDynamoDBClientBuilder.standard().withEndpointConfiguration(

                    new AwsClientBuilder.EndpointConfiguration("http://localhost:8000", "eu-central-1"))
                    .build();

        setupDBSchema(dynamodb);
    }


    public void setupDBSchema(AmazonDynamoDB dynamodb){

        CreateTableResult res = createTable(dynamodb, tablePrefix + "Example", "Id");
        listTables(dynamodb.listTables(), "DynamoDB Local over HTTP");
    }

    private static void listTables(ListTablesResult result, String method) {
        System.out.println("found " + Integer.toString(result.getTableNames().size()) + " tables with " + method);
        for(String table : result.getTableNames()) {
            System.out.println(table);
        }
    }

    private static CreateTableResult createTable(AmazonDynamoDB ddb, String tableName, String hashKeyName) {
        List<AttributeDefinition> attributeDefinitions = new ArrayList<AttributeDefinition>();
        attributeDefinitions.add(new AttributeDefinition(hashKeyName, ScalarAttributeType.S));

        List<KeySchemaElement> ks = new ArrayList<KeySchemaElement>();
        ks.add(new KeySchemaElement(hashKeyName, KeyType.HASH));

        ProvisionedThroughput provisionedthroughput = new ProvisionedThroughput(1000L, 1000L);

        CreateTableRequest request =
                new CreateTableRequest()
                        .withTableName(tableName)
                        .withAttributeDefinitions(attributeDefinitions)
                        .withKeySchema(ks)
                        .withProvisionedThroughput(provisionedthroughput);

        return ddb.createTable(request);
    }
    }

I then access the tables using basic SpringData-based RestController and Repository classes after setting amazon.dynamodb.tableNameOverride=dev_ . in my properties file. I expected the mapping to access "dev_Example" due to the override instead of "Example", which is stated in the model class via @DynamoDBTable(tableName = "Example") .

Actual Behavior When I now try to GET any information from the Example table, I get this error:

com.amazonaws.services.dynamodbv2.model.ResourceNotFoundException: Cannot do operations on a non-existent table (Service: AmazonDynamoDBv2; Status Code: 400; Error Code: ResourceNotFoundException; Request ID: b600800b-0e21-4793-8220-78febf0aead0)

because the application still tries to access "Example" instead of "dev_Example" which was created in the runner.

Steps to Reproduce the Problem

  1. Start up application with mentioned configuration
  2. call any REST endpoint accessing data from the "Example"-table.

Specifications

  • Spring Data DynamoDB Version:
<dependency>
			<groupId>com.github.derjust</groupId>
			<artifactId>spring-data-dynamodb</artifactId>
			<version>5.0.2</version>
		</dependency>
		<dependency>
			<groupId>com.amazonaws</groupId>
			<artifactId>DynamoDBLocal</artifactId>
			<version>[1.11,2.0)</version>
		</dependency>
  • Spring Data Version: 2.0.3.RELEASE
  • AWS SDK Version:
<dependency>
			<groupId>com.amazonaws</groupId>
			<artifactId>aws-java-sdk-dynamodb</artifactId>
			<version>1.11.351</version>
		</dependency>
  • Java Version: 10.0.1 - Java HotSpot(TM) 64-Bit Server VM 10.0.1+10
  • Platform Details: Running from IntelliJ IDEA Spring plugin for Maven.

I tried multiple different versions, some including the stuff that is referenced in the wiki, but the prefix is never applied to my queries once my application start up.

naumannt avatar Jul 23 '18 14:07 naumannt

Might help some people but after reviewing #59

I've found that it still works (in 5.03) using XML config.

Created a demo repo that might help people here:

https://github.com/clos-consultancy/SpringDynamoDB

eggsy84 avatar Aug 30 '18 15:08 eggsy84

Hello.

I've been checking this today, with the same approach that you have. The only thing I changed is the name of the property on application.properties file and the way to instantiate the DynamoDBMapperConfig object.

I've used amazon.dynamodb.tableNamePrefix instead of amazon.dynamodb.tableNameOverride. It seems that this last one redefines the table name. I'm not sure anyways on that.

I made up the name of the property amazon.dynamodb.tableNamePrefix. Don't know if exists haha.

On the configuration class, I have.

@Configuration
@EnableDynamoDBRepositories(
        dynamoDBMapperConfigRef = "dynamoDBMapperConfig",
        basePackages = "package.of.repo")
public class DynamoDBConfig {

    private static final String SEPARATOR = "-";

    @Value("${amazon.dynamodb.tableNamePrefix}")
    private String tablePrefix;

    @Bean
    public AmazonDynamoDB amazonDynamoDB() {
        return AmazonDynamoDBClientBuilder
                .standard()
                .withRegion(Regions.fromName(Environment.getRegion()))
                .build();
    }

    @Bean
    public DynamoDBMapperConfig dynamoDBMapperConfig() {
        DynamoDBMapperConfig.Builder builder = DynamoDBMapperConfig.builder();
        builder.setTableNameOverride(DynamoDBMapperConfig.TableNameOverride.withTableNamePrefix(getFormattedPrefix()));
        return builder.build();
    }

    @Bean
    public DynamoDBMapper dynamoDBMapper(AmazonDynamoDB amazonDynamoDB, DynamoDBMapperConfig config) {
        return new DynamoDBMapper(amazonDynamoDB, config);
    }

    private String getFormattedPrefix(){
        return tablePrefix + SEPARATOR;
    }
}

Maybe this could help you too.

GonzaloSaad avatar Oct 04 '18 21:10 GonzaloSaad

Any luck?

isereb avatar Oct 21 '19 21:10 isereb

Hey.

I've been using the approach I wrote a year ago. For me it works pretty good. I have several lambdas on a project with that.

https://github.com/uvsy-aws-backend/java-dynamo-client/blob/e03cc850d5495de991f51bb5475114015b656eb3/src/main/java/com/universy/dynamo/mappercreators/DynamoDBAccess.java#L19

That's the main lib from which I configure the mapper. Not using SpringData now, but is the same idea.

I would like to know why I got downvoted haha

GonzaloSaad avatar Oct 21 '19 21:10 GonzaloSaad

I am using DynamoDB spring data addon, and I wanted to add a table name prefix depending on environment (dev/prod). Unfortunately, I assume, it will never be possible for older version of the client.

isereb avatar Oct 22 '19 13:10 isereb

Do you want to share some code? We can give it a try!

GonzaloSaad avatar Oct 22 '19 13:10 GonzaloSaad

I stopped using derjust's library because of this issue. And it is not as hard as I thought to re-implement those methods. Thanks for your advice, @GonzaloSaad!

public class YourDynamoRepository implements CrudRepository<YourDynamoEntity, Long> {

    private final DynamoDBMapper mapper; // bean injected by spring

    // One of the repository methods
    @Override public <S extends YourDynamoEntity> S save(S s) {
        mapper.save(s);
        return s;
    }
}

isereb avatar Oct 22 '19 14:10 isereb

Alright. Good to hear that worked! Anyways, I believe with that workaround you can keep using it!

GonzaloSaad avatar Oct 22 '19 14:10 GonzaloSaad