diff --git a/src/main/java/org/socialsignin/spring/data/dynamodb/repository/Query.java b/src/main/java/org/socialsignin/spring/data/dynamodb/repository/Query.java index 4b5d67af..fd97d0f7 100644 --- a/src/main/java/org/socialsignin/spring/data/dynamodb/repository/Query.java +++ b/src/main/java/org/socialsignin/spring/data/dynamodb/repository/Query.java @@ -20,7 +20,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; - +import org.socialsignin.spring.data.dynamodb.repository.QueryConstants.ConsistentReadMode; import static org.socialsignin.spring.data.dynamodb.repository.QueryConstants.QUERY_LIMIT_UNLIMITED; @Retention(RetentionPolicy.RUNTIME) @@ -47,4 +47,7 @@ * Expressions */ int limit() default QUERY_LIMIT_UNLIMITED; + + + ConsistentReadMode consistentReads() default ConsistentReadMode.DEFAULT; } diff --git a/src/main/java/org/socialsignin/spring/data/dynamodb/repository/QueryConstants.java b/src/main/java/org/socialsignin/spring/data/dynamodb/repository/QueryConstants.java index ada9855d..79bbf655 100644 --- a/src/main/java/org/socialsignin/spring/data/dynamodb/repository/QueryConstants.java +++ b/src/main/java/org/socialsignin/spring/data/dynamodb/repository/QueryConstants.java @@ -15,11 +15,16 @@ */ package org.socialsignin.spring.data.dynamodb.repository; -public final class QueryConstants { +public class QueryConstants { private QueryConstants() { } public static final int QUERY_LIMIT_UNLIMITED = Integer.MIN_VALUE; + public enum ConsistentReadMode { + DEFAULT, + CONSISTENT, + EVENTUAL + } } diff --git a/src/main/java/org/socialsignin/spring/data/dynamodb/repository/query/AbstractDynamoDBQueryCreator.java b/src/main/java/org/socialsignin/spring/data/dynamodb/repository/query/AbstractDynamoDBQueryCreator.java index 4b7ae4ba..05538943 100644 --- a/src/main/java/org/socialsignin/spring/data/dynamodb/repository/query/AbstractDynamoDBQueryCreator.java +++ b/src/main/java/org/socialsignin/spring/data/dynamodb/repository/query/AbstractDynamoDBQueryCreator.java @@ -19,6 +19,7 @@ import com.amazonaws.services.dynamodbv2.model.ComparisonOperator; import org.socialsignin.spring.data.dynamodb.core.DynamoDBOperations; import org.socialsignin.spring.data.dynamodb.query.Query; +import org.socialsignin.spring.data.dynamodb.repository.QueryConstants; import org.socialsignin.spring.data.dynamodb.repository.support.DynamoDBEntityInformation; import org.socialsignin.spring.data.dynamodb.repository.support.DynamoDBIdIsHashAndRangeKeyEntityInformation; import org.springframework.data.mapping.PropertyPath; @@ -47,23 +48,26 @@ public abstract class AbstractDynamoDBQueryCreator protected final DynamoDBOperations dynamoDBOperations; protected final Optional projection; protected final Optional limit; + protected final QueryConstants.ConsistentReadMode consistentReads; public AbstractDynamoDBQueryCreator(PartTree tree, DynamoDBEntityInformation entityMetadata, - Optional projection, Optional limitResults, DynamoDBOperations dynamoDBOperations) { + Optional projection, Optional limitResults, QueryConstants.ConsistentReadMode consistentReads, DynamoDBOperations dynamoDBOperations) { super(tree); this.entityMetadata = entityMetadata; this.projection = projection; this.limit = limitResults; + this.consistentReads = consistentReads; this.dynamoDBOperations = dynamoDBOperations; } public AbstractDynamoDBQueryCreator(PartTree tree, ParameterAccessor parameterAccessor, - DynamoDBEntityInformation entityMetadata, Optional projection, - Optional limitResults, DynamoDBOperations dynamoDBOperations) { + DynamoDBEntityInformation entityMetadata, Optional projection, + Optional limitResults, QueryConstants.ConsistentReadMode consistentReads, DynamoDBOperations dynamoDBOperations) { super(tree, parameterAccessor); this.entityMetadata = entityMetadata; this.projection = projection; this.limit = limitResults; + this.consistentReads = consistentReads; this.dynamoDBOperations = dynamoDBOperations; } diff --git a/src/main/java/org/socialsignin/spring/data/dynamodb/repository/query/AbstractDynamoDBQueryCriteria.java b/src/main/java/org/socialsignin/spring/data/dynamodb/repository/query/AbstractDynamoDBQueryCriteria.java index 6aa69d6b..340aa28c 100644 --- a/src/main/java/org/socialsignin/spring/data/dynamodb/repository/query/AbstractDynamoDBQueryCriteria.java +++ b/src/main/java/org/socialsignin/spring/data/dynamodb/repository/query/AbstractDynamoDBQueryCriteria.java @@ -29,6 +29,7 @@ import org.socialsignin.spring.data.dynamodb.marshaller.Date2IsoDynamoDBMarshaller; import org.socialsignin.spring.data.dynamodb.marshaller.Instant2IsoDynamoDBMarshaller; import org.socialsignin.spring.data.dynamodb.query.Query; +import org.socialsignin.spring.data.dynamodb.repository.QueryConstants; import org.socialsignin.spring.data.dynamodb.repository.support.DynamoDBEntityInformation; import org.socialsignin.spring.data.dynamodb.utils.SortHandler; import org.springframework.data.domain.Sort; @@ -73,6 +74,7 @@ public abstract class AbstractDynamoDBQueryCriteria implements DynamoDBQu protected Sort sort = Sort.unsorted(); protected Optional projection = Optional.empty(); protected Optional limit = Optional.empty(); + protected QueryConstants.ConsistentReadMode consistentReads = QueryConstants.ConsistentReadMode.DEFAULT; public abstract boolean isApplicableForLoad(); @@ -134,6 +136,17 @@ protected QueryRequest buildQueryRequest(String tableName, String theIndexName, } limit.ifPresent(queryRequest::setLimit); + + switch (consistentReads) { + case CONSISTENT: + queryRequest.setConsistentRead(true); + break; + case EVENTUAL: + queryRequest.setConsistentRead(false); + break; + default: + break; + } applySortIfSpecified(queryRequest, new ArrayList<>(new HashSet<>(allowedSortProperties))); } return queryRequest; @@ -703,4 +716,10 @@ public DynamoDBQueryCriteria withLimit(Optional limit) { this.limit = limit; return this; } + + @Override + public DynamoDBQueryCriteria withConsistentReads(QueryConstants.ConsistentReadMode consistentReads) { + this.consistentReads = consistentReads; + return this; + } } diff --git a/src/main/java/org/socialsignin/spring/data/dynamodb/repository/query/DynamoDBCountQueryCreator.java b/src/main/java/org/socialsignin/spring/data/dynamodb/repository/query/DynamoDBCountQueryCreator.java index 8df657bf..7975e3b3 100644 --- a/src/main/java/org/socialsignin/spring/data/dynamodb/repository/query/DynamoDBCountQueryCreator.java +++ b/src/main/java/org/socialsignin/spring/data/dynamodb/repository/query/DynamoDBCountQueryCreator.java @@ -18,6 +18,7 @@ import org.socialsignin.spring.data.dynamodb.core.DynamoDBOperations; import org.socialsignin.spring.data.dynamodb.query.Query; import org.socialsignin.spring.data.dynamodb.query.StaticQuery; +import org.socialsignin.spring.data.dynamodb.repository.QueryConstants; import org.socialsignin.spring.data.dynamodb.repository.support.DynamoDBEntityInformation; import org.springframework.data.domain.Sort; import org.springframework.data.repository.query.ParameterAccessor; @@ -32,13 +33,13 @@ public class DynamoDBCountQueryCreator extends AbstractDynamoDBQueryCreat public DynamoDBCountQueryCreator(PartTree tree, DynamoDBEntityInformation entityMetadata, DynamoDBOperations dynamoDBOperations, boolean pageQuery) { - super(tree, entityMetadata, Optional.empty(), Optional.empty(), dynamoDBOperations); + super(tree, entityMetadata, Optional.empty(), Optional.empty(), QueryConstants.ConsistentReadMode.DEFAULT, dynamoDBOperations); this.pageQuery = pageQuery; } public DynamoDBCountQueryCreator(PartTree tree, ParameterAccessor parameterAccessor, DynamoDBEntityInformation entityMetadata, DynamoDBOperations dynamoDBOperations, boolean pageQuery) { - super(tree, parameterAccessor, entityMetadata, Optional.empty(), Optional.empty(), dynamoDBOperations); + super(tree, parameterAccessor, entityMetadata, Optional.empty(), Optional.empty(), QueryConstants.ConsistentReadMode.DEFAULT, dynamoDBOperations); this.pageQuery = pageQuery; } diff --git a/src/main/java/org/socialsignin/spring/data/dynamodb/repository/query/DynamoDBQueryCreator.java b/src/main/java/org/socialsignin/spring/data/dynamodb/repository/query/DynamoDBQueryCreator.java index 26b236fc..a2113061 100644 --- a/src/main/java/org/socialsignin/spring/data/dynamodb/repository/query/DynamoDBQueryCreator.java +++ b/src/main/java/org/socialsignin/spring/data/dynamodb/repository/query/DynamoDBQueryCreator.java @@ -18,6 +18,7 @@ import org.socialsignin.spring.data.dynamodb.core.DynamoDBOperations; import org.socialsignin.spring.data.dynamodb.query.Query; import org.socialsignin.spring.data.dynamodb.query.StaticQuery; +import org.socialsignin.spring.data.dynamodb.repository.QueryConstants; import org.socialsignin.spring.data.dynamodb.repository.support.DynamoDBEntityInformation; import org.springframework.data.domain.Sort; import org.springframework.data.repository.query.ParameterAccessor; @@ -29,9 +30,9 @@ public class DynamoDBQueryCreator extends AbstractDynamoDBQueryCreator { public DynamoDBQueryCreator(PartTree tree, ParameterAccessor parameterAccessor, - DynamoDBEntityInformation entityMetadata, Optional projection, Optional limit, + DynamoDBEntityInformation entityMetadata, Optional projection, Optional limit, QueryConstants.ConsistentReadMode consistentReads, DynamoDBOperations dynamoDBOperations) { - super(tree, parameterAccessor, entityMetadata, projection, limit, dynamoDBOperations); + super(tree, parameterAccessor, entityMetadata, projection, limit, consistentReads, dynamoDBOperations); } @Override @@ -42,6 +43,7 @@ protected Query complete(@Nullable DynamoDBQueryCriteria criteria, Sor criteria.withSort(sort); criteria.withProjection(projection); criteria.withLimit(limit); + criteria.withConsistentReads(consistentReads); return criteria.buildQuery(dynamoDBOperations); } } diff --git a/src/main/java/org/socialsignin/spring/data/dynamodb/repository/query/DynamoDBQueryCriteria.java b/src/main/java/org/socialsignin/spring/data/dynamodb/repository/query/DynamoDBQueryCriteria.java index 876c7664..246c89eb 100644 --- a/src/main/java/org/socialsignin/spring/data/dynamodb/repository/query/DynamoDBQueryCriteria.java +++ b/src/main/java/org/socialsignin/spring/data/dynamodb/repository/query/DynamoDBQueryCriteria.java @@ -18,6 +18,7 @@ import com.amazonaws.services.dynamodbv2.model.ComparisonOperator; import org.socialsignin.spring.data.dynamodb.core.DynamoDBOperations; import org.socialsignin.spring.data.dynamodb.query.Query; +import org.socialsignin.spring.data.dynamodb.repository.QueryConstants; import org.springframework.data.domain.Sort; import java.util.Optional; @@ -45,6 +46,8 @@ DynamoDBQueryCriteria withSingleValueCriteria(String propertyName, Compar DynamoDBQueryCriteria withLimit(Optional limit); + DynamoDBQueryCriteria withConsistentReads(QueryConstants.ConsistentReadMode reads); + Query buildQuery(DynamoDBOperations dynamoDBOperations); Query buildCountQuery(DynamoDBOperations dynamoDBOperations, boolean pageQuery); diff --git a/src/main/java/org/socialsignin/spring/data/dynamodb/repository/query/DynamoDBQueryMethod.java b/src/main/java/org/socialsignin/spring/data/dynamodb/repository/query/DynamoDBQueryMethod.java index e8743d41..f246b366 100644 --- a/src/main/java/org/socialsignin/spring/data/dynamodb/repository/query/DynamoDBQueryMethod.java +++ b/src/main/java/org/socialsignin/spring/data/dynamodb/repository/query/DynamoDBQueryMethod.java @@ -18,6 +18,7 @@ import org.socialsignin.spring.data.dynamodb.repository.EnableScan; import org.socialsignin.spring.data.dynamodb.repository.EnableScanCount; import org.socialsignin.spring.data.dynamodb.repository.Query; +import org.socialsignin.spring.data.dynamodb.repository.QueryConstants; import org.socialsignin.spring.data.dynamodb.repository.support.DynamoDBEntityInformation; import org.socialsignin.spring.data.dynamodb.repository.support.DynamoDBEntityMetadataSupport; import org.springframework.data.projection.ProjectionFactory; @@ -41,6 +42,7 @@ public class DynamoDBQueryMethod extends QueryMethod { private final boolean scanCountEnabledForRepository; private final Optional projectionExpression; private final Optional limitResults; + private QueryConstants.ConsistentReadMode consistentReadMode; public DynamoDBQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFactory factory) { super(method, metadata, factory); @@ -63,9 +65,11 @@ public DynamoDBQueryMethod(Method method, RepositoryMetadata metadata, Projectio } else { this.limitResults = Optional.empty(); } + this.consistentReadMode = query.consistentReads(); } else { this.projectionExpression = Optional.empty(); this.limitResults = Optional.empty(); + this.consistentReadMode = QueryConstants.ConsistentReadMode.DEFAULT; } } @@ -111,4 +115,8 @@ public Optional getProjectionExpression() { public Optional getLimitResults() { return this.limitResults; } + + public QueryConstants.ConsistentReadMode getConsistentReadMode() { + return this.consistentReadMode; + } } diff --git a/src/main/java/org/socialsignin/spring/data/dynamodb/repository/query/PartTreeDynamoDBQuery.java b/src/main/java/org/socialsignin/spring/data/dynamodb/repository/query/PartTreeDynamoDBQuery.java index 52b3d642..2d6a1c5b 100644 --- a/src/main/java/org/socialsignin/spring/data/dynamodb/repository/query/PartTreeDynamoDBQuery.java +++ b/src/main/java/org/socialsignin/spring/data/dynamodb/repository/query/PartTreeDynamoDBQuery.java @@ -39,7 +39,7 @@ public PartTreeDynamoDBQuery(DynamoDBOperations dynamoDBOperations, DynamoDBQuer protected DynamoDBQueryCreator createQueryCreator(ParametersParameterAccessor accessor) { return new DynamoDBQueryCreator<>(tree, accessor, getQueryMethod().getEntityInformation(), - getQueryMethod().getProjectionExpression(), getQueryMethod().getLimitResults(), dynamoDBOperations); + getQueryMethod().getProjectionExpression(), getQueryMethod().getLimitResults(), getQueryMethod().getConsistentReadMode(), dynamoDBOperations); } protected DynamoDBCountQueryCreator createCountQueryCreator(ParametersParameterAccessor accessor, diff --git a/src/test/java/org/socialsignin/spring/data/dynamodb/domain/sample/UserRepository.java b/src/test/java/org/socialsignin/spring/data/dynamodb/domain/sample/UserRepository.java index eb90eef1..6dd96ccb 100644 --- a/src/test/java/org/socialsignin/spring/data/dynamodb/domain/sample/UserRepository.java +++ b/src/test/java/org/socialsignin/spring/data/dynamodb/domain/sample/UserRepository.java @@ -17,6 +17,7 @@ import org.socialsignin.spring.data.dynamodb.repository.EnableScan; import org.socialsignin.spring.data.dynamodb.repository.Query; +import org.socialsignin.spring.data.dynamodb.repository.QueryConstants; import org.springframework.data.repository.CrudRepository; import java.time.Instant; @@ -31,6 +32,7 @@ public interface UserRepository extends CrudRepository { Iterable findAll(); // CRUD method using Optional + @Query(consistentReads = QueryConstants.ConsistentReadMode.CONSISTENT) Optional findById(String id); @EnableScan diff --git a/src/test/java/org/socialsignin/spring/data/dynamodb/repository/query/PartTreeDynamoDBQueryUnitTest.java b/src/test/java/org/socialsignin/spring/data/dynamodb/repository/query/PartTreeDynamoDBQueryUnitTest.java index 37d58e2b..c03f9252 100644 --- a/src/test/java/org/socialsignin/spring/data/dynamodb/repository/query/PartTreeDynamoDBQueryUnitTest.java +++ b/src/test/java/org/socialsignin/spring/data/dynamodb/repository/query/PartTreeDynamoDBQueryUnitTest.java @@ -35,6 +35,7 @@ import org.socialsignin.spring.data.dynamodb.domain.sample.Playlist; import org.socialsignin.spring.data.dynamodb.domain.sample.PlaylistId; import org.socialsignin.spring.data.dynamodb.domain.sample.User; +import org.socialsignin.spring.data.dynamodb.repository.QueryConstants; import org.socialsignin.spring.data.dynamodb.repository.support.DynamoDBEntityInformation; import org.socialsignin.spring.data.dynamodb.repository.support.DynamoDBIdIsHashAndRangeKeyEntityInformation; import org.springframework.data.repository.query.Parameter; @@ -115,6 +116,7 @@ public void setUp() { Mockito.when(mockPlaylistEntityMetadata.getIndexRangeKeyPropertyNames()).thenReturn(new HashSet()); Mockito.when(mockDynamoDBUserQueryMethod.getEntityInformation()).thenReturn(mockUserEntityMetadata); Mockito.when(mockDynamoDBUserQueryMethod.getParameters()).thenReturn(mockParameters); + Mockito.when(mockDynamoDBUserQueryMethod.getConsistentReadMode()).thenReturn(QueryConstants.ConsistentReadMode.DEFAULT); Mockito.when(mockDynamoDBPlaylistQueryMethod.getEntityInformation()).thenReturn(mockPlaylistEntityMetadata); Mockito.when(mockDynamoDBPlaylistQueryMethod.getParameters()).thenReturn(mockParameters); Mockito.when(mockUserEntityMetadata.getHashKeyPropertyName()).thenReturn("id"); @@ -142,6 +144,7 @@ private void setupCommonMocksForThisRepositoryMetho Mockito.when(mockDynamoDBQueryMethod.getEntityType()).thenReturn(clazz); Mockito.when(mockDynamoDBQueryMethod.getName()).thenReturn(repositoryMethodName); Mockito.when(mockDynamoDBQueryMethod.getParameters()).thenReturn(mockParameters); + Mockito.when(mockDynamoDBQueryMethod.getConsistentReadMode()).thenReturn(QueryConstants.ConsistentReadMode.DEFAULT); Mockito.when(mockParameters.getBindableParameters()).thenReturn(mockParameters); Mockito.when(mockParameters.getNumberOfParameters()).thenReturn(numberOfParameters); // Mockito.when(mockDynamoDBQueryMethod.getReturnedObjectType()).thenReturn(clazz);