Java – How to query DynamoDB tables by hash using Spring Data

How to query DynamoDB tables by hash using Spring Data… here is a solution to the problem.

How to query DynamoDB tables by hash using Spring Data

I have a DynamoDB table with the following keys [customerid (HASH), sku (RANGE)].

I’m trying to query only by customerid. I’m currently getting this error:

Caused by: java.lang.IllegalStateException: You have defined query method in the repository but you don't have any query lookup strategy defined. The infrastructure apparently does not support query methods!
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.<init>(RepositoryFactorySupport.java:545) ~[ spring-data-commons-2.1.0.RELEASE.jar:2.1.0.RELEASE]
    at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:324) ~[spring-data-commons-2.1.0.RELEASE.jar :2.1.0.RELEASE]
    at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.lambda$afterPropertiesSet$5(RepositoryFactoryBeanSupport.java:297) ~[ spring-data-commons-2.1.0.RELEASE.jar:2.1.0.RELEASE]
    at org.springframework.data.util.Lazy.getNullable(Lazy.java:211) ~[spring-data-commons-2.1.0.RELEASE.jar:2.1.0.RELEASE]
    at org.springframework.data.util.Lazy.get(Lazy.java:94) ~[spring-data-commons-2.1.0.RELEASE.jar:2.1.0.RELEASE]
    at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:300) ~[ spring-data-commons-2.1.0.RELEASE.jar:2.1.0.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1804) ~[ spring-beans-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1741) ~[ spring-beans-5.1.3.RELEASE.jar:5.1.3.RELEASE]
    ... 30 common frames omitted

I’ve added queryLookupStrategy to my app and hopefully it will work. Several values were tried, but the message persisted.

@SpringBootApplication
@EnableDynamoDBRepositories(queryLookupStrategy = QueryLookupStrategy.Key.CREATE_IF_NOT_FOUND)
public class ProductItemApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProductItemApplication.class, args);
    }
}

Is this possible? Or Dynamo’s limitations? Since Cassandra can easily do this, I’m assuming Dynamo can do it too, but probably wrong.

Do I need to define an index with only customerid to query this? If so, what’s the best way to do this in code using spring-data?

Or is this a limitation of Dynamo’s spring data, and to achieve this, I need to explicitly use a Dynamo client instead of spring data?

My repository is defined like this:

interface CustomerItemRepository extends CrudRepository<CustomerItem, CustomerItemId> {
    List<CustomerItem> findByCustomerId(String customerId);
}

My key is:

@NoArgsConstructor
@AllArgsConstructor
public class CustomerItemId {
    private String customerId;
    private String sku;

@DynamoDBHashKey(attributeName = "customerId")
    public String getCustomerId() {
        return customerId;
    }

@SuppressWarnings("unused")
    public void setCustomerId(String customerId) {
        this.customerId = customerId;
    }

@DynamoDBRangeKey(attributeName = "sku")
    public String getSku() {
        return sku;
    }

@SuppressWarnings("unused")
    public void setSku(String sku) {
        this.sku = sku;
    }
}

Finally, my project is:

    @DynamoDBTable(tableName = "CustomerItem")
    @NoArgsConstructor
    public class CustomerItem {
        @Id
        private CustomerItemId id;

//... a few other non key fields...

CustomerItem(String customerId, String sku) {
            this.id = new CustomerItemId(customerId, sku);
        }

@DynamoDBHashKey(attributeName = "customerId")
        public String getCustomerId() {
            return id.getCustomerId();
        }

@DynamoDBRangeKey(attributeName = "sku")
        public String getSku() {
            return id.getSku();
        }

public void setCustomerId(String customerId) {
            this.id = new CustomerItemId(customerId, id != null ? id.getSku() : null);
        }

public void setSku(String sku) {
            this.id = new CustomerItemId(id != null ? id.getCustomerId() : null, sku);
        }
    }

What is the most elegant way to solve this problem? Dynamo seems like the right technology to store this simple data cheaply, but now I think I’d better use something else.

Solution

I

know this doesn’t solve your problem, but for Java projects, I’ll use DynamoDBMapper for object persistence, not Spring. It provides a very clean way to map ordinary objects to DynamoDB and is provided and supported by AWS. I use it a lot and DynamoDBMapper is fantastic.

Related Problems and Solutions