当更新请求中不存在GSI的范围键属性时,DynamoDBMapper ConditionalCheckFailedException [英] DynamoDBMapper ConditionalCheckFailedException when range-key attribute for GSI is not present in update request

查看:126
本文介绍了当更新请求中不存在GSI的范围键属性时,DynamoDBMapper ConditionalCheckFailedException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正试图找出在将DynamoDBMapper与特定的保存表达式以及UPDATE_SKIP_NULL_ATTRIBUTES SaveBehavior一起使用时收到的ConditionalCheckFailedException的原因.

I'm trying to reason about the cause of a ConditionalCheckFailedException I receive when using DynamoDBMapper with a specific save expression and with UPDATE_SKIP_NULL_ATTRIBUTES SaveBehavior.

我的架构如下:

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表中创建/更新/获取记录.

I use the following class to create/update/get the records in the members table.

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)
        );
    }
}

我编写了一个测试类,用于将记录插入和更新到成员表中,如下所示:

I wrote a test class to insert and update records into the members table, which is as follows:

 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所示.

As can be seen above, if I do not pass a valid value for joinDate(which happens to be the range-key for the groups GSI), in the updateEntry call, DynamoDB returns a ConditionalCheckFailedException. This is the case, even when I use a save behavior of UPDATE_SKIP_NULL_ATTRIBUTES, as can be seen in DDBModelDAO.java.

有人可以帮助我理解,为什么必须发送GSI的range-key属性才能成功进行条件写操作吗?

Can someone help me understand, why I'm required to send the range-key attribute for the GSI, for a conditional write to succeed?

推荐答案

不确定是否能回答您的问题:接口DynamoDBAutoGenerator :

Not sure if this answers your question: Interface DynamoDBAutoGenerator:

"DynamoDBAutoGenerateStrategy.CREATE,指示何时生成创建项目.映射器,确定一个项目是新的,或者如果当前值为null,则覆盖.当有限制时使用其中之一执行部分更新,DynamoDBMapperConfig.SaveBehavior.UPDATE_SKIP_NULL_ATTRIBUTES或DynamoDBMapperConfig.SaveBehavior.APPEND_SET.仅当映射器也在生成密钥时,才会生成一个新值."

"DynamoDBAutoGenerateStrategy.CREATE, instructs to generate when creating the item. The mapper, determines an item is new, or overwriting, if it's current value is null. There is a limitation when performing partial updates using either, DynamoDBMapperConfig.SaveBehavior.UPDATE_SKIP_NULL_ATTRIBUTES, or DynamoDBMapperConfig.SaveBehavior.APPEND_SET. A new value will only be generated if the mapper is also generating the key."

所以最后一部分很重要:仅当映射器也在生成密钥时,才会生成新值"那应该可以解释为什么您只看到自己正在经历的行为.

So the last part is important: "A new value will only be generated if the mapper is also generating the key" That should explain why you only see the behavior that you are experiencing.

这有意义吗?

这篇关于当更新请求中不存在GSI的范围键属性时,DynamoDBMapper ConditionalCheckFailedException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆