当更新请求中不存在GSI的范围键属性时,DynamoDBMapper ConditionalCheckFailedException [英] DynamoDBMapper ConditionalCheckFailedException when range-key attribute for GSI is not present in update request
问题描述
我正试图找出在将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屋!