DynamoDBMapper如何在同一列上指定多个条件以进行保存操作

时间:2017-01-06 00:36:09

标签: amazon-dynamodb

假设我们有一个名为lastEventId (String)的Dynamodb表。我们只有在满足以下条件之一时才更新ddb行

if ( lastEventId does not exists OR lastEventId == 123(let's say) ).

如何使用DynamoDBMapper API在同一列上指定多个条件。

尝试使用dynamoDBMapper.save(ModelClass, DynamoDbSaveExpression)

2 个答案:

答案 0 :(得分:1)

我意识到这是一个古老的问题,但希望它能帮助别人,这就是我必须做的事情;

DynamoDBMapper不支持链接条件 即它不支持条件表达式 更新if(a = b AND(c<> d或e> f))。使用Mapper,所有条件都是ANDed或ORed在一起。 要支持更复杂的需求,您需要使用低级API中的putItem或updateItem。

这是一个java示例: 假设我们有记录,其中哈希键是userId,范围键是创建的时间戳。 如果哈希键不存在,或者它确实存在并且字段THREAD_ID具有特定值,我们想要记录一条记录。

这是我在从postgres db到dynamo进行数据迁移时遇到的真实情况。 postgres db 以1秒的粒度存储创建的时间戳,并使用threadId作为主键。当然,当我们尝试的时候 使用userId / created元组作为哈希/范围键同步这些记录,然后使用相同的userId / created创建所有记录 关键只是相互覆盖 - 而不是我们想要的。

起初我尝试使用updateItem,但是没有用(因为我还没有发现)。到底 我需要使用putItem一些黑客 - 如果你有想法如何改进这些代码 - 请大声喊叫!

    //Capture the threadId of the new record
    Map<String, AttributeValue> eav = new HashMap();
    eav.put(":" + THREAD_ID, new AttributeValue().withS(record.getThreadId()));

    Map<String, AttributeValue> attributeValues = record.getAtributeValues(); //See example below;

    PutItemRequest putItemRequest = new PutItemRequest()
        .withTableName(configuration.getTableName())
        .withItem(attributeValues)
        .withExpressionAttributeValues(eav)
        .withReturnValues(ReturnValue.ALL_OLD) //If nothing is written this will return a result with null attributes
        .withConditionExpression("(attribute_not_exists(" + USER_ID +
            ") AND attribute_not_exists(" + CREATED +
            ")) OR (attribute_exists(" + USER_ID +
            ") AND attribute_exists(" + CREATED +
            ") AND " + THREAD_ID + " = :" + THREAD_ID + ")");

    int count = 0;
    int maxTries = 50; //There really shouldn't be 50 records for the same user within 50 ms of each other. If there is, increase this number
    while(true) {
      try {
        //It seems that due to the condition expression the putItem will only work if there is an item to compare to (uncomfirmed).
        //If there isn't a record, it does nothing and returns a result with null attributes.
        //In that case we must save normally.  I've not found a way to do this in one pass....
        PutItemResult putItemResult = client.putItem(putItemRequest);
        if (putItemResult.getAttributes() == null)
          mapper.save(record);
        break;
      } catch (ConditionalCheckFailedException ce) {
        //In this case a record already exists with this hash/range key combination.
        //Increment the created timestamp and try again.
        record.setCreated(record.getCreated() + 1);
        //We must reset the putItemRequest values to reflect the new created value.
        attributeValues = record.getAtributeValues();
        putItemRequest.withItem(attributeValues);
        if (++count == maxTries)
          throw new InternalServerErrorException(
              new TError("Duplicate userId / created error after " + maxTries + "attempts for userId " + record.userId + " and created " + record.getCreated()));
      }
    }

  //Attributes must not be null
  @DynamoDBIgnore
  public Map<String, AttributeValue> getAtributeValues() {
    Map<String, AttributeValue> attributeValueHashMap = new HashMap<>();
    //The hash, range key
    if (!Strings.isNullOrEmpty(this.userId))
      attributeValueHashMap.put( USER_ID, new AttributeValue().withS(this.userId));
    if (this.created > 0)
      attributeValueHashMap.put( CREATED, new AttributeValue().withN(Long.toString(this.created)));

    //Record values
    if (!Strings.isNullOrEmpty(this.authorId))
      attributeValueHashMap.put( AUTHOR_ID, new AttributeValue().withS(this.authorId));
    if (!Strings.isNullOrEmpty(this.notificationText))
      attributeValueHashMap.put( NOTIFICATION_TEXT, new AttributeValue().withS(this.notificationText));
    if (!Strings.isNullOrEmpty(this.threadId))
      attributeValueHashMap.put( THREAD_ID, new AttributeValue().withS(this.threadId));
    //etc for other params

    return attributeValueHashMap;
  }

答案 1 :(得分:-2)

您只需使用以下保存表达式。

dynamoDBMapper.save(ModelClass);

无需使用DynamoDbSaveExpression。因为您的要求是保存对象。

save的作用类似于hibernates saveorupdate操作。

如果lastEventId不可用,则会保存为新行。

如果lastEventId已存在,则会更新其他更改。

N.B:

但你必须知道主键(散列键和范围键)。

hash key = primary key

hash key + range key = primary key. It works like composite key.

资源链接:

  1. Insert DynamoDB Items with DynamoDBMapper
  2. DynamoDBMapper for conditional saves