GSI Queries can fail with NPE when using an overriding DynamoDBMapperConfig
See original GitHub issueQuerying a Global Secondary Index will fail if using an overriding DynamoDBMapperConfig that doesnt specify a ConversionSchema and TypeConverterFactory.
Expected Behavior
Querying a Global Secondary Index will not fail if using an overriding DynamoDBMapperConfig that doesnt specify a ConversionSchema and TypeConverterFactory (perhaps by using reasonable defaults).
OR
An appropriate exception is thrown indicating an invalid configuration.
Actual Behavior
Querying a Global Secondary Index of a table when using a custom DynamoDBMapperConfig that does not specify a ConversionSchema and TypeConverterFactory fails with a NullPointerException. Searches on the regular table itself execute correctly.
Steps to Reproduce the Problem
- Create the DynamoDB beans with a custom DynamoDBMapperConfig
@Primary
public DynamoDBMapperConfig dynamoDBMapperConfig() {
String fullPrefix = tablePrefix + TABLE_DELIMITER + environment + TABLE_DELIMITER;
TableNameOverride tableNameOverride = TableNameOverride.withTableNamePrefix(fullPrefix);
return DynamoDBMapperConfig.builder()
.withTableNameOverride(tableNameOverride)
.build();
}
@Bean
@Primary
public DynamoDBMapper dynamoDBMapper(AmazonDynamoDB amazonDynamoDB, DynamoDBMapperConfig config,
DynamoDBMapper mapper) {
return new DynamoDBMapper(amazonDynamoDB, config);
}
@Bean
public AmazonDynamoDB amazonDynamoDB() {
return AmazonDynamoDBClientBuilder
.standard()
.withCredentials(amazonAWSCredentialsProvider())
.withRegion(Regions.fromName(awsRegion))
.build();
}
- Create the domain object that specifies the Global Secondary Index
@Data
@DynamoDBTable(tableName = "shopper")
public class Shopper{
@DynamoDBHashKey(attributeName = "id")
@DynamoDBAutoGeneratedKey
private String id;
@DynamoDBAttribute(attributeName = "address-hash" )
@DynamoDBIndexHashKey(globalSecondaryIndexName = "idx-address-hash")
private String addressHash;
@DynamoDBTypeConvertedJson
@DynamoDBAttribute(attributeName = "address")
private Address address;
}
- Create the Repository Bean with the custom find method
@Repository
@EnableScan
public interface ShopperRepository extends CrudRepository<Shopper, String> {
Shopper findByAddressHash(String addressHash);
}
- Invoke the method during runtime
Shopper result = repository.findByAddressHash(addressHash);
- Observe the resulting stack trace
java.lang.NullPointerException: null
at java.util.concurrent.ConcurrentHashMap.get(ConcurrentHashMap.java:936) ~[na:1.8.0_66]
at java.util.concurrent.ConcurrentHashMap.containsKey(ConcurrentHashMap.java:964) ~[na:1.8.0_66]
at com.amazonaws.services.dynamodbv2.datamodeling.StandardModelFactories$StandardModelFactory.getTableFactory(StandardModelFactories.java:82) ~[aws-java-sdk-dynamodb-1.11.613.jar:na]
at com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper.getTableModel(DynamoDBMapper.java:410) ~[aws-java-sdk-dynamodb-1.11.613.jar:na]
at org.socialsignin.spring.data.dynamodb.core.DynamoDBTemplate.getTableModel(DynamoDBTemplate.java:223) ~[spring-data-dynamodb-5.1.0.jar:5.1.0]
at org.socialsignin.spring.data.dynamodb.repository.query.AbstractDynamoDBQueryCreator.create(AbstractDynamoDBQueryCreator.java:69) ~[spring-data-dynamodb-5.1.0.jar:5.1.0]
at org.socialsignin.spring.data.dynamodb.repository.query.AbstractDynamoDBQueryCreator.create(AbstractDynamoDBQueryCreator.java:42) ~[spring-data-dynamodb-5.1.0.jar:5.1.0]
at org.springframework.data.repository.query.parser.AbstractQueryCreator.createCriteria(AbstractQueryCreator.java:119) ~[spring-data-commons-2.1.10.RELEASE.jar:2.1.10.RELEASE]
at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:95) ~[spring-data-commons-2.1.10.RELEASE.jar:2.1.10.RELEASE]
at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:81) ~[spring-data-commons-2.1.10.RELEASE.jar:2.1.10.RELEASE]
at org.socialsignin.spring.data.dynamodb.repository.query.PartTreeDynamoDBQuery.doCreateQuery(PartTreeDynamoDBQuery.java:56) ~[spring-data-dynamodb-5.1.0.jar:5.1.0]
at org.socialsignin.spring.data.dynamodb.repository.query.AbstractDynamoDBQuery.doCreateQueryWithPermissions(AbstractDynamoDBQuery.java:81) ~[spring-data-dynamodb-5.1.0.jar:5.1.0]
at org.socialsignin.spring.data.dynamodb.repository.query.AbstractDynamoDBQuery$SingleEntityExecution.execute(AbstractDynamoDBQuery.java:282) ~[spring-data-dynamodb-5.1.0.jar:5.1.0]
at org.socialsignin.spring.data.dynamodb.repository.query.AbstractDynamoDBQuery.execute(AbstractDynamoDBQuery.java:311) ~[spring-data-dynamodb-5.1.0.jar:5.1.0]
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:605) ~[spring-data-commons-2.1.10.RELEASE.jar:2.1.10.RELEASE]
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.lambda$invoke$3(RepositoryFactorySupport.java:595) ~[spring-data-commons-2.1.10.RELEASE.jar:2.1.10.RELEASE]
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:595) ~[spring-data-commons-2.1.10.RELEASE.jar:2.1.10.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:59) ~[spring-data-commons-2.1.10.RELEASE.jar:2.1.10.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93) ~[spring-aop-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:61) ~[spring-data-commons-2.1.10.RELEASE.jar:2.1.10.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) ~[spring-aop-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at com.sun.proxy.$Proxy78.findByAddressHash(Unknown Source) ~[na:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_66]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_66]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_66]
at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_66]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343) ~[spring-aop-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) ~[spring-aop-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139) ~[spring-tx-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) ~[spring-aop-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at com.sun.proxy.$Proxy78.findByAddressHash(Unknown Source) ~[na:na]
...
Specifications
- Spring Data DynamoDB Version: 5.1.0 (2.1)
- Spring Data Version: 2.1.10.RELEASE
- AWS SDK Version: 1.11.613
- Java Version: 1.8.0_66 - Java HotSpot™ 64-Bit Server VM 25.66-b17
- Platform Details: Mac OS X 10.14.5
Additional Information
- I have enabled
spring.main.allow-bean-definition-overriding
- I was able to work around this issue by setting the ConversionSchema and TypeConverterFactory on my DynamoDBMapperConfig:
@Bean
@Primary
public DynamoDBMapperConfig dynamoDBMapperConfig() {
String fullPrefix = tablePrefix + TABLE_DELIMITER + environment + TABLE_DELIMITER;
TableNameOverride tableNameOverride = TableNameOverride.withTableNamePrefix(fullPrefix);
return DynamoDBMapperConfig.builder()
.withTableNameOverride(tableNameOverride)
.withConversionSchema(ConversionSchemas.V2)
.withTypeConverterFactory(DynamoDBTypeConverterFactory.standard())
.build();
}
Issue Analytics
- State:
- Created 4 years ago
- Reactions:6
- Comments:5
Top Results From Across the Web
DynamoDB, with boostchicken lib, GSI with override table ...
I'm using the Java lib https://github.com/boostchicken/spring-data-dynamodb to handle dynamoDB connection with spring boot.
Read more >spring-data-dynamodb - Bountysource
Querying a Global Secondary Index will fail if using an overriding DynamoDBMapperConfig that doesnt specify a ConversionSchema and TypeConverterFactory.
Read more >Querying global secondary indexes with SDK for Java 1.x
You can query a global secondary index in the following ways: Use the queryIndex method on the QueryIndexDax class defined in the following...
Read more >Using DynamoDB on Spring Boot (feat.Kotlin)
In this post, I will talk about how to use Amazon DynamoDB with Spring Boot application made with Kotlin.
Read more >DynamoDB Mapper GSI Query Example in Java
Trying to figure out how to perform a GSI Query on your AWS DynamoDB Table using DynamoDB Mapper? This is the article for...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
you also need to add
@EnableDynamoDBRepositories(dynamoDBMapperConfigRef = "dynamoDBMapperConfig")
at AwsConfiguration class level.I had a similar problem, adding
.withTypeConverterFactory(DynamoDBTypeConverterFactory.standard())
seems to have solved the issue.