Skip to content

Commit 4e4d105

Browse files
committed
Fix for issue #62 - Support DynamoDBMapper v2 annotations (@DynamoDBTypeConverted, @DynamoDBTyped etc.) instead of/in addition to @DynamoDBMarshalling for query methods.
Signed-off-by: Alex Arana <[email protected]>
1 parent deef49a commit 4e4d105

9 files changed

+81
-35
lines changed

src/main/java/org/socialsignin/spring/data/dynamodb/core/DynamoDBOperations.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import java.util.List;
44
import java.util.Map;
55

6+
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperTableModel;
67
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBQueryExpression;
78
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBScanExpression;
89
import com.amazonaws.services.dynamodbv2.datamodeling.KeyPair;
@@ -35,5 +36,11 @@ public interface DynamoDBOperations {
3536

3637
public <T> String getOverriddenTableName(Class<T> domainClass, String tableName);
3738

38-
39+
/**
40+
* Provides access to the DynamoDB mapper table model of the underlying domain type.
41+
*
42+
* @param domainClass A domain type
43+
* @return Corresponding DynamoDB table model
44+
*/
45+
<T> DynamoDBMapperTableModel<T> getTableModel(Class<T> domainClass);
3946
}

src/main/java/org/socialsignin/spring/data/dynamodb/core/DynamoDBTemplate.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
2121
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper;
2222
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperConfig;
23+
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperTableModel;
2324
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBQueryExpression;
2425
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBScanExpression;
2526
import com.amazonaws.services.dynamodbv2.datamodeling.KeyPair;
@@ -224,7 +225,15 @@ public <T> String getOverriddenTableName(Class<T> domainClass, String tableName)
224225

225226
return tableName;
226227
}
227-
228+
229+
/**
230+
* {@inheritDoc}
231+
*/
232+
@Override
233+
public <T> DynamoDBMapperTableModel<T> getTableModel(Class<T> domainClass) {
234+
return dynamoDBMapper.getTableModel(domainClass, dynamoDBMapperConfig);
235+
}
236+
228237
protected <T> void maybeEmitEvent(DynamoDBMappingEvent<T> event) {
229238
if (null != eventPublisher) {
230239
eventPublisher.publishEvent(event);

src/main/java/org/socialsignin/spring/data/dynamodb/repository/query/AbstractDynamoDBQueryCreator.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.springframework.util.ClassUtils;
3434
import org.springframework.util.ObjectUtils;
3535

36+
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperTableModel;
3637
import com.amazonaws.services.dynamodbv2.model.ComparisonOperator;
3738

3839
/**
@@ -60,10 +61,10 @@ public AbstractDynamoDBQueryCreator(PartTree tree, ParameterAccessor parameterAc
6061

6162
@Override
6263
protected DynamoDBQueryCriteria<T, ID> create(Part part, Iterator<Object> iterator) {
63-
64+
final DynamoDBMapperTableModel<T> tableModel = dynamoDBOperations.getTableModel(entityMetadata.getJavaType());
6465
DynamoDBQueryCriteria<T, ID> criteria = entityMetadata.isRangeKeyAware() ? new DynamoDBEntityWithHashAndRangeKeyCriteria<T, ID>(
65-
(DynamoDBIdIsHashAndRangeKeyEntityInformation<T, ID>) entityMetadata)
66-
: new DynamoDBEntityWithHashKeyOnlyCriteria<T, ID>(entityMetadata);
66+
(DynamoDBIdIsHashAndRangeKeyEntityInformation<T, ID>) entityMetadata, tableModel)
67+
: new DynamoDBEntityWithHashKeyOnlyCriteria<T, ID>(entityMetadata, tableModel);
6768
return addCriteria(criteria, part, iterator);
6869
}
6970

src/main/java/org/socialsignin/spring/data/dynamodb/repository/query/AbstractDynamoDBQueryCriteria.java

Lines changed: 39 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
import org.springframework.util.LinkedMultiValueMap;
4141
import org.springframework.util.MultiValueMap;
4242

43+
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperFieldModel;
44+
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperTableModel;
4345
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMarshaller;
4446
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBQueryExpression;
4547
import com.amazonaws.services.dynamodbv2.model.AttributeValue;
@@ -56,6 +58,7 @@ public abstract class AbstractDynamoDBQueryCriteria<T, ID extends Serializable>
5658
protected Class<T> clazz;
5759
private DynamoDBEntityInformation<T, ID> entityInformation;
5860
private Map<String, String> attributeNamesByPropertyName;
61+
private final DynamoDBMapperTableModel<T> tableModel;
5962
private String hashKeyPropertyName;
6063

6164
protected MultiValueMap<String, Condition> attributeConditions;
@@ -211,14 +214,15 @@ protected List<Condition> getHashKeyConditions() {
211214
return hashKeyConditions;
212215
}
213216

214-
public AbstractDynamoDBQueryCriteria(DynamoDBEntityInformation<T, ID> dynamoDBEntityInformation) {
217+
public AbstractDynamoDBQueryCriteria(DynamoDBEntityInformation<T, ID> dynamoDBEntityInformation, final DynamoDBMapperTableModel<T> tableModel) {
215218
this.clazz = dynamoDBEntityInformation.getJavaType();
216219
this.attributeConditions = new LinkedMultiValueMap<String, Condition>();
217220
this.propertyConditions = new LinkedMultiValueMap<String, Condition>();
218221
this.hashKeyPropertyName = dynamoDBEntityInformation.getHashKeyPropertyName();
219222
this.entityInformation = dynamoDBEntityInformation;
220223
this.attributeNamesByPropertyName = new HashMap<String, String>();
221-
224+
// TODO consider adding the DynamoDBMapper table model to DynamoDBEntityInformation instead
225+
this.tableModel = tableModel;
222226
}
223227

224228
private String getFirstDeclaredIndexNameForAttribute(Map<String,String[]> indexNamesByAttributeName,List<String> indexNamesToCheck,String attributeName)
@@ -479,16 +483,22 @@ public DynamoDBQueryCriteria<T, ID> withCondition(String propertyName, Condition
479483
return this;
480484
}
481485

482-
@SuppressWarnings("unchecked")
483-
protected <V> Object getPropertyAttributeValue(String propertyName, Object value) {
484-
DynamoDBMarshaller<V> marshaller = (DynamoDBMarshaller<V>) entityInformation.getMarshallerForProperty(propertyName);
486+
@SuppressWarnings({"deprecation", "unchecked"})
487+
protected <V extends Object> Object getPropertyAttributeValue(final String propertyName, final V value) {
488+
// TODO consider removing DynamoDBMarshaller code altogether as table model will handle accordingly
489+
final DynamoDBMarshaller<V> marshaller = (DynamoDBMarshaller<V>) entityInformation.getMarshallerForProperty(propertyName);
485490

486-
if (marshaller != null) {
487-
return marshaller.marshall((V) value);
488-
} else {
489-
return value;
490-
}
491-
}
491+
if (marshaller != null) {
492+
return marshaller.marshall(value);
493+
} else if (tableModel != null) { // purely here for testing as DynamoDBMapperTableModel cannot be mocked using Mockito
494+
DynamoDBMapperFieldModel<T,Object> fieldModel = tableModel.field(propertyName);
495+
if (fieldModel != null) {
496+
return fieldModel.convert(value);
497+
}
498+
}
499+
500+
return value;
501+
}
492502

493503
protected <V> Condition createNoValueCondition(String propertyName, ComparisonOperator comparisonOperator) {
494504

@@ -626,16 +636,19 @@ protected Condition createSingleValueCondition(String propertyName, ComparisonOp
626636
Assert.notNull(o, "Creating conditions on null property values not supported: please specify a value for '"
627637
+ propertyName + "'");
628638

639+
List<AttributeValue> attributeValueList = new ArrayList<AttributeValue>();
629640
Object attributeValue = !alreadyMarshalledIfRequired ? getPropertyAttributeValue(propertyName, o) : o;
641+
if (ClassUtils.isAssignableValue(AttributeValue.class, attributeValue)) {
642+
attributeValueList.add((AttributeValue) attributeValue);
643+
} else {
644+
boolean marshalled = !alreadyMarshalledIfRequired && attributeValue != o
645+
&& !entityInformation.isCompositeHashAndRangeKeyProperty(propertyName);
630646

631-
boolean marshalled = !alreadyMarshalledIfRequired && attributeValue != o
632-
&& !entityInformation.isCompositeHashAndRangeKeyProperty(propertyName);
647+
Class<?> targetPropertyType = marshalled ? String.class : propertyType;
648+
attributeValueList = addAttributeValue(attributeValueList, attributeValue, propertyName, targetPropertyType, true);
649+
}
633650

634-
Class<?> targetPropertyType = marshalled ? String.class : propertyType;
635-
List<AttributeValue> attributeValueList = new ArrayList<AttributeValue>();
636-
attributeValueList = addAttributeValue(attributeValueList, attributeValue, propertyName, targetPropertyType, true);
637651
return new Condition().withComparisonOperator(comparisonOperator).withAttributeValueList(attributeValueList);
638-
639652
}
640653

641654
protected Condition createCollectionCondition(String propertyName, ComparisonOperator comparisonOperator, Iterable<?> o,
@@ -647,12 +660,15 @@ protected Condition createCollectionCondition(String propertyName, ComparisonOpe
647660
boolean marshalled = false;
648661
for (Object object : o) {
649662
Object attributeValue = getPropertyAttributeValue(propertyName, object);
650-
if (attributeValue != null) {
651-
marshalled = attributeValue != object && !entityInformation.isCompositeHashAndRangeKeyProperty(propertyName);
652-
}
653-
Class<?> targetPropertyType = marshalled ? String.class : propertyType;
654-
attributeValueList = addAttributeValue(attributeValueList, attributeValue, propertyName, targetPropertyType, false);
655-
663+
if (ClassUtils.isAssignableValue(AttributeValue.class, attributeValue)) {
664+
attributeValueList.add((AttributeValue) attributeValue);
665+
} else {
666+
if (attributeValue != null) {
667+
marshalled = attributeValue != object && !entityInformation.isCompositeHashAndRangeKeyProperty(propertyName);
668+
}
669+
Class<?> targetPropertyType = marshalled ? String.class : propertyType;
670+
attributeValueList = addAttributeValue(attributeValueList, attributeValue, propertyName, targetPropertyType, false);
671+
}
656672
}
657673

658674
return new Condition().withComparisonOperator(comparisonOperator).withAttributeValueList(attributeValueList);

src/main/java/org/socialsignin/spring/data/dynamodb/repository/query/DynamoDBEntityWithHashAndRangeKeyCriteria.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.socialsignin.spring.data.dynamodb.repository.support.DynamoDBIdIsHashAndRangeKeyEntityInformation;
3939
import org.springframework.util.Assert;
4040

41+
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperTableModel;
4142
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBQueryExpression;
4243
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBScanExpression;
4344
import com.amazonaws.services.dynamodbv2.model.ComparisonOperator;
@@ -67,8 +68,10 @@ protected boolean isRangeKeyProperty(String propertyName) {
6768
return rangeKeyPropertyName.equals(propertyName);
6869
}
6970

70-
public DynamoDBEntityWithHashAndRangeKeyCriteria(DynamoDBIdIsHashAndRangeKeyEntityInformation<T, ID> entityInformation) {
71-
super(entityInformation);
71+
public DynamoDBEntityWithHashAndRangeKeyCriteria(
72+
DynamoDBIdIsHashAndRangeKeyEntityInformation<T, ID> entityInformation, DynamoDBMapperTableModel<T> tableModel) {
73+
74+
super(entityInformation, tableModel);
7275
this.rangeKeyPropertyName = entityInformation.getRangeKeyPropertyName();
7376
this.indexRangeKeyPropertyNames = entityInformation.getIndexRangeKeyPropertyNames();
7477
if (indexRangeKeyPropertyNames == null) {

src/main/java/org/socialsignin/spring/data/dynamodb/repository/query/DynamoDBEntityWithHashKeyOnlyCriteria.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.socialsignin.spring.data.dynamodb.query.SingleEntityLoadByHashKeyQuery;
3030
import org.socialsignin.spring.data.dynamodb.repository.support.DynamoDBEntityInformation;
3131

32+
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperTableModel;
3233
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBScanExpression;
3334
import com.amazonaws.services.dynamodbv2.model.ComparisonOperator;
3435
import com.amazonaws.services.dynamodbv2.model.Condition;
@@ -41,8 +42,8 @@ public class DynamoDBEntityWithHashKeyOnlyCriteria<T, ID extends Serializable> e
4142

4243
private DynamoDBEntityInformation<T, ID> entityInformation;
4344

44-
public DynamoDBEntityWithHashKeyOnlyCriteria(DynamoDBEntityInformation<T, ID> entityInformation) {
45-
super(entityInformation);
45+
public DynamoDBEntityWithHashKeyOnlyCriteria(DynamoDBEntityInformation<T, ID> entityInformation, DynamoDBMapperTableModel<T> tableModel) {
46+
super(entityInformation, tableModel);
4647
this.entityInformation = entityInformation;
4748
}
4849

src/test/java/org/socialsignin/spring/data/dynamodb/domain/sample/FeedUser.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey;
1010
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBIndexHashKey;
1111
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBIndexRangeKey;
12+
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperFieldModel.DynamoDBAttributeType;
1213
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBNativeBoolean;
1314
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable;
15+
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTyped;
1416

1517
@DynamoDBTable(tableName = "feed_user")
1618
public class FeedUser {
@@ -29,7 +31,14 @@ public class FeedUser {
2931
private Date feedRegDate;
3032

3133
@DynamoDBAttribute
32-
@DynamoDBNativeBoolean
34+
@DynamoDBTyped(DynamoDBAttributeType.N) // For backwards compatibility with old versions, DynamoDBMapper booleans are
35+
// serialized using the DynamoDB N type so this is not strictly required. This
36+
// is here purely to test the DynamoDBMapper v2 conversion schemas using new
37+
// non-deprecated annotations. NOTE: this introduces an important change in
38+
// *internal* behaviour where @DynamoDBNativeBoolean will no longer be translated
39+
// to DynamoDB type N. Instead, it would be correctly mapped to type BOOL which
40+
// would cause this test to fail given that only scalar (B, N or S) types are
41+
// allowed key types
3342
@DynamoDBIndexRangeKey(globalSecondaryIndexName = "idx_global_usrNo_feedOpenYn")
3443
private boolean feedOpenYn;
3544

src/test/java/org/socialsignin/spring/data/dynamodb/repository/query/DynamoDBEntityWithHashAndRangeKeyCriteriaUnitTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public void setUp()
2222
{
2323
Mockito.when(entityInformation.getHashKeyPropertyName()).thenReturn("userName");
2424
Mockito.when(entityInformation.getRangeKeyPropertyName()).thenReturn("playlistName");
25-
criteria = new DynamoDBEntityWithHashAndRangeKeyCriteria<Playlist,String>(entityInformation);
25+
criteria = new DynamoDBEntityWithHashAndRangeKeyCriteria<Playlist,String>(entityInformation, null);
2626
}
2727

2828
@Test

src/test/java/org/socialsignin/spring/data/dynamodb/repository/query/DynamoDBEntityWithHashKeyOnlyCriteriaUnitTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public class DynamoDBEntityWithHashKeyOnlyCriteriaUnitTest extends AbstractDynam
2323
public void setUp()
2424
{
2525
Mockito.when(entityInformation.getHashKeyPropertyName()).thenReturn("id");
26-
criteria = new DynamoDBEntityWithHashKeyOnlyCriteria<User,String>(entityInformation);
26+
criteria = new DynamoDBEntityWithHashKeyOnlyCriteria<User,String>(entityInformation, null);
2727
}
2828

2929
@Test

0 commit comments

Comments
 (0)