我试图推断使用带有特定保存表达式的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的范围键属性,以使条件写入成功?
答案 0 :(得分:0)
不确定这是否回答了您的问题: Interface DynamoDBAutoGenerator:
“DynamoDBAutoGenerateStrategy.CREATE,指示生成时间 创建项目。映射器确定项目是新的,或 覆盖,如果它的当前值为null。有时有限制 使用其中任何一个执 DynamoDBMapperConfig.SaveBehavior.UPDATE_SKIP_NULL_ATTRIBUTES,或DynamoDBMapperConfig.SaveBehavior.APPEND_SET。只有在映射器也生成密钥时才会生成新值。“
所以最后一部分很重要:“只有在映射器也生成密钥时才会生成新值” 这应该可以解释为什么你只看到你正在经历的行为。
这有意义吗?