当更新请求

时间:2017-04-08 09:09:24

标签: amazon-web-services amazon-dynamodb

我试图推断使用带有特定保存表达式的DynamoDBMapper和UPDATE_SKIP_NULL_ATTRIBUTES SaveBehavior时收到的ConditionalCheckFailedException的原因。

我的架构如下:

Member.java

@Data
@DynamoDBTable(tableName = "members")
public class Member implements DDBTable {

    private static final String GROUP_GSI_NAME = "group-gsi";

    @DynamoDBHashKey
    @DynamoDBAutoGeneratedKey
    private String memberId;

    @DynamoDBVersionAttribute
    private Long version;

    @DynamoDBIndexHashKey(globalSecondaryIndexName = GROUP_GSI_NAME)
    private String groupId;

    @DynamoDBAutoGeneratedTimestamp(strategy = DynamoDBAutoGenerateStrategy.CREATE)
    @DynamoDBIndexRangeKey(globalSecondaryIndexName = GROUP_GSI_NAME)
    private Date joinDate;

    @DynamoDBAttribute
    private String memberName;

    @Override
    @DynamoDBIgnore
    public String getHashKeyColumnName() {
        return "memberId";
    }

    @Override
    @DynamoDBIgnore
    public String getHashKeyColumnValue() {
        return memberId;
    }
}

我使用以下类在members表中创建/更新/获取记录。

DDBModelDAO.java

public class DDBModelDAO<T extends DDBTable> {

    private final Class<T> ddbTableClass;
    private final AmazonDynamoDB amazonDynamoDB;
    private final DynamoDBMapper dynamoDBMapper;

    public DDBModelDAO(Class<T> ddbTableClass, AmazonDynamoDB amazonDynamoDB, DynamoDBMapper dynamoDBMapper) {
        this.ddbTableClass = ddbTableClass;
        this.amazonDynamoDB = amazonDynamoDB;
        this.dynamoDBMapper = dynamoDBMapper;
    }

    public T loadEntry(final String hashKey) {
        return dynamoDBMapper.load(ddbTableClass, hashKey);
    }

    public T createEntry(final T item) {
        dynamoDBMapper.save(item, getSaveExpressionForCreate(item));
        return item;
    }

    public T updateEntry(final T item) {
        dynamoDBMapper.save(item, getSaveExpressionForUpdate(item),
             DynamoDBMapperConfig.SaveBehavior.UPDATE_SKIP_NULL_ATTRIBUTES.config());
       return item;
    }

    private DynamoDBSaveExpression getSaveExpressionForCreate(final T item) {
        // No record with the same hash key must be present when creating
        return new DynamoDBSaveExpression().withExpectedEntry(item.getHashKeyColumnName(),
            new ExpectedAttributeValue(false));
    }

    private DynamoDBSaveExpression getSaveExpressionForUpdate(final T item) {
        // The hash key for the record being updated must be present.
        return new DynamoDBSaveExpression().withExpectedEntry(item.getHashKeyColumnName(),
            new ExpectedAttributeValue(new AttributeValue(item.getHashKeyColumnValue()))
                    .withComparisonOperator(ComparisonOperator.EQ)
        );
    }
}

我写了一个测试类来插入和更新成员表中的记录,如下所示:

 public static void main(String[] args) {
    DDBTestClient testClient = new DDBTestClient();

    AmazonDynamoDB amazonDynamoDB = testClient.buildAmazonDynamoDB();
    DynamoDBMapper dynamoDBMapper = testClient.buildDynamoDBMapper(amazonDynamoDB);

    DDBModelDAO<Member> memberDAO = new DDBModelDAO<>(Member.class, amazonDynamoDB, dynamoDBMapper);
    DDBModelDAO<Group> groupDAO = new DDBModelDAO<>(Group.class, amazonDynamoDB, dynamoDBMapper);

    try {
        // Create a group
        Group groupToCreate = new Group();
        groupToCreate.setGroupName("group-0");
        Group createdGroup = groupDAO.createEntry(groupToCreate);
        System.out.println("Created group: " + createdGroup);

        Thread.sleep(3000);

        // Create a member for the group
        Member memberToCreate = new Member();
        memberToCreate.setGroupId(createdGroup.getGroupId());
        memberToCreate.setMemberName("member-0");
        Member createdMember = memberDAO.createEntry(memberToCreate);
        System.out.println("Created member: " + createdMember);

        Thread.sleep(3000);

        // Update member name
        createdMember.setMemberName("member-updated-0");
        createdMember.setGroupId(null);
        //createdMember.setJoinDate(null); // <---- Causes ConditionalCheckFailedException
        memberDAO.updateEntry(createdMember);
        System.out.println("Updated member");

    } catch (Exception exception) {
        System.out.println(exception.getMessage());
    }
}

如上所示,如果我没有为joinDate传递有效值(恰好是组GSI的范围键),则在updateEntry调用中,DynamoDB会返回ConditionalCheckFailedException。即使我使用UPDATE_SKIP_NULL_ATTRIBUTES的保存行为,也是如此,如DDBModelDAO.java中所示。

有人可以帮助我理解,为什么我需要发送GSI的范围键属性,以使条件写入成功?

1 个答案:

答案 0 :(得分:0)

不确定这是否回答了您的问题: Interface DynamoDBAutoGenerator

“DynamoDBAutoGenerateStrategy.CREATE,指示生成时间    创建项目。映射器确定项目是新的,或    覆盖,如果它的当前值为null。有时有限制    使用其中任何一个执  DynamoDBMapperConfig.SaveBehavior.UPDATE_SKIP_NULL_ATTRIBUTES,或DynamoDBMapperConfig.SaveBehavior.APPEND_SET。只有在映射器也生成密钥时才会生成新值。“

所以最后一部分很重要:“只有在映射器也生成密钥时才会生成新值” 这应该可以解释为什么你只看到你正在经历的行为。

这有意义吗?