AbstractDynamoDao extends AbstractDao<M,P,K,I> { protected UpdateItemSpec toUpdateItemSpec(K id, Update<P> update, Instant updateAt, I updaterId) { String updateExpression = ""; NameMap nameMap = new NameMap(); ValueMap valueMap = new ValueMap(); Item setFields = partialMapper.toItem(update.getPartial()); Map<String,Object> set = new LinkedHashMap<>(); for (String field : update.getSetFields()) { set.put(field, setFields.get(field)); } Field updatedByField = getFieldData().getUpdatedBy(); if (updatedByField != null && updaterId != null) { set.put(updatedByField.getStorageName(), toStorageValue(updaterId)); } Field updatedAtField = getFieldData().getUpdatedAt(); if (updatedAtField != null && updateAt != null) { set.put(updatedAtField.getStorageName(), toStorageValue(updateAt)); } for (Map.Entry<String,Object> e : set.entrySet()) { String key = e.getKey(); nameMap.put("#" + key, key); valueMap.put(":" + key, toStorageValue(e.getValue())); updateExpression += "#" + key + " = :" + key + ", "; } if (updateExpression.length() > 0) { updateExpression = "SET " + updateExpression.substring(0, updateExpression.length()-2); } if (update.getRemoveFields().size() > 0) { updateExpression += " REMOVE "; for (String key : update.getRemoveFields()) { nameMap.put("#" + key, key); updateExpression += "#" + key + ", "; } updateExpression = updateExpression.substring(0, updateExpression.length()-2); } UpdateItemSpec spec = new UpdateItemSpec() .withPrimaryKey(toPrimaryKey(id)) .withUpdateExpression(updateExpression) .withNameMap(nameMap); if (!valueMap.isEmpty()) { spec = spec.withValueMap(valueMap); } return spec; } AbstractDynamoDao(Class<M> modelClass, Class<P> partialClass, AmazonDynamoDB db, String tableName);  AbstractDynamoDao(Class<M> modelClass, Class<P> partialClass, AmazonDynamoDB db, String tableName, ItemMapper<M> modelMapper, ItemMapper<P> partialMapper); String getTableName(); Table getTable(); Optional<M> loadOpt(K id); Optional<P> loadOpt(K id, Iterable<String> fields); @Override Iterable<M> iterateAll(); Results<M,K> scanAll(); @Override M create(P partial, Instant createAt, I creatorId); void update(K id, Update<P> update, Instant updateAt, I updaterId); M updateAndReturn(K id, Update<P> update, Instant updateAt, I updaterId); Update<P> updateOf(P partial); Update<P> updateOf(P partial, Iterable<String> removeFields); @Override void delete(K id); abstract List<KeySchemaElement> getKeySchema(); abstract List<AttributeDefinition> getAttributeDefinitions(); List<GlobalSecondaryIndex> getGlobalSecondaryIndexes(); void initTable(); void dropTable(); void copyTable(AmazonDynamoDB sourceDb, String sourceTable); void ensureIndexes();  }
@Test public void testToUpdateItemSpec() throws Exception { UUID updateId = UUID.randomUUID(); UUID updaterId = UUID.randomUUID(); Instant now = Instant.now(); Update<TestModel> update = Update.of( TestModel.builder().name("Test").birthday(LocalDate.of(1985, 10, 26)).country("US").build(), ImmutableSet.of("name", "birthday"), ImmutableSet.of("email")); UpdateItemSpec spec = dao.toUpdateItemSpec(updateId, update, now, updaterId); assertEquals("SET #name = :name, #birthday = :birthday, #updateId = :updateId, #updateAt = :updateAt REMOVE #email", spec.getUpdateExpression()); assertEquals(updaterId.toString(), spec.getValueMap().get(":updateId")); assertEquals("Test", spec.getValueMap().get(":name")); assertEquals(updaterId.toString(), spec.getValueMap().get(":updateId")); assertEquals(new Long(now.toEpochMilli()), spec.getValueMap().get(":updateAt")); }