dynamodb analyst (1)- how to implement “part” update

设置一个attribute为null(或有时候没有设置)时,保存数据(org.springframework.data.repository.CrudRepository#save)的执行行为:

一般情况下,会走入下面的分支:

com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper.SaveObjectHandler#onNullNonKeyAttribute


@Override
protected void onNullNonKeyAttribute(String attributeName) {
/*
* If UPDATE_SKIP_NULL_ATTRIBUTES or APPEND_SET is
* configured, we don't delete null value attributes.
*/
if (getLocalSaveBehavior() == SaveBehavior.UPDATE_SKIP_NULL_ATTRIBUTES
|| getLocalSaveBehavior() == SaveBehavior.APPEND_SET) {
return;
}

else {
/* Delete attributes that are set as null in the object. */
getAttributeValueUpdates()
.put(attributeName,
new AttributeValueUpdate()
.withAction("DELETE"));
}
}

因为默认保存行为为:SaveBehavior.UPDATE.所以其实会删掉对应的attribute的值,而不是不管之前attribute是否有值,从这点看,试图通过部分attribute设置非Null值、部分attribute设置Null来达到“部分”(patch)更新是做不到的。

仔细查阅上面代码,反过来说明只要修改默认保存行为为UPDATE_SKIP_NULL_ATTRIBUTES,就可以忽略Null值。

副作用:假设一个值之前有值,后来想改成Null,那就实现不了了。所以如果使用UPDATE_SKIP_NULL_ATTRIBUTES,用户场景不能有这种。

实际上,在不修改默认保存方法的情况下,还有另外一种实现“部分”更新的方法,就是提供另外一个专门的entity对象,去除不想更新的attributes。这样也可以实现部分更新。

另外要注意批量Save其实是直接覆盖:

批量save:

com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper#batchWrite

本质上用的是PUT:

for ( Object toWrite : objectsToWrite ) { 

//*******

requestItems.add(tableName, new WriteRequest(new PutRequest(transformAttributes(parameters))));
}

而普通的save:
com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper#save

默认用的不是PUT.


        boolean usePut = (finalConfig.getSaveBehavior() == SaveBehavior.CLOBBER
                         || finalConfig.getSaveBehavior() == SaveBehavior.PUT)
                         || anyKeyGeneratable(model, object, finalConfig.getSaveBehavior());

        SaveObjectHandler saveObjectHandler;

        if (usePut) {
              //默认不走入这个分支。


所以:
BATCH SAVE其实是直接覆盖,SAVE可以选择直接覆盖还是部分更新(Update)。个人觉得这个行为不好,不一致。
追根溯源: 批量BatchWriteItemRequest是com.amazonaws.services.dynamodbv2.model.WriteRequest的批量,这个类只接受PUT和DELETE两种Request作为构造器参数。

   public WriteRequest(PutRequest putRequest) {
        setPutRequest(putRequest);
    }

   public WriteRequest(DeleteRequest deleteRequest) {
        setDeleteRequest(deleteRequest);
   }

发布者

傅, 健

程序员,Java、C++、开源爱好者. About me