假设我们有一个名为lastEventId (String)
的Dynamodb表。我们只有在满足以下条件之一时才更新ddb行
if ( lastEventId does not exists OR lastEventId == 123(let's say) ).
如何使用DynamoDBMapper API在同一列上指定多个条件。
尝试使用dynamoDBMapper.save(ModelClass, DynamoDbSaveExpression)
答案 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已存在,则会更新其他更改。
但你必须知道主键(散列键和范围键)。
hash key = primary key
hash key + range key = primary key. It works like composite key.