如何使用DynamoDBMapper为DDB列表属性添加元素(含初始化)
问题描述
我的DDB表结构如下:
primaryKey: primaryKeyValue secondaryKey: secondaryKeyValue key3 : ["abc", "cde", "fgh"]
当前更新数据库的代码实现:
private DynamoDBMapperConfig saveConfig = DynamoDBMapperConfig.builder() .withSaveBehavior(DynamoDBMapperConfig.SaveBehavior.UPDATE_SKIP_NULL_ATTRIBUTES) .build(); public void createOrUpdateTest(ExternalTestMetadata testMetadata) { log.info("Saving test metadata {} into DDB.", testMetadata); try { dynamoDBMapper.save(testMetadata, saveConfig); } catch (Exception e) { String errorMessage = String.format("Failed to save test metadata into DDB, testId: {%s}. Reason: %s", testMetadata.getTestId(), e.getMessage()); log.error(errorMessage, e); throw new DependencyException(errorMessage, e); } }
调用代码:
TableName.TableNameBuilder tableNameBuilder = TableName.builder() .primaryKey(primaryKeyValue) .secondaryKey(secondaryKeyValue); tableNameMapper.createOrUpdateTest(tableNameBuilder.build());
需求:给key3添加字符串,若该key不存在则创建列表并将其作为第一个元素,若已存在则追加到列表中。请问是否必须使用APPEND_SET?该如何实现?
解决方案
核心结论
不需要必须用APPEND_SET,直接用现有save方法无法安全实现需求(存在并发更新丢失风险),最优方案是用DynamoDB的UpdateExpression配合LIST_APPEND和if_not_exists函数实现原子操作。
具体实现
放弃dynamoDBMapper.save(),改用updateItem方法完成列表的追加/初始化:
public void appendToKey3(String primaryKeyValue, String secondaryKeyValue, String newString) { // 定义表达式参数 Map<String, AttributeValue> expressionAttributeValues = new HashMap<>(); // 空列表,用于key3不存在时初始化 expressionAttributeValues.put(":emptyList", AttributeValue.builder().l().build()); // 要追加的新元素(LIST_APPEND默认追加到尾部,如需加到头部,调换list_append的两个参数顺序即可) expressionAttributeValues.put(":newItem", AttributeValue.builder().l( AttributeValue.builder().s(newString).build() ).build()); // 构建更新表达式 UpdateExpression updateExpression = UpdateExpression.builder() .set("key3") .value(ExpressionValue.function("list_append", ExpressionValue.function("if_not_exists", ExpressionValue.path("key3"), ExpressionValue.value(":emptyList")), ExpressionValue.value(":newItem") )) .build(); // 构建主键条件 Map<String, AttributeValue> key = new HashMap<>(); key.put("primaryKey", AttributeValue.builder().s(primaryKeyValue).build()); key.put("secondaryKey", AttributeValue.builder().s(secondaryKeyValue).build()); try { dynamoDBMapper.updateItem(ExternalTestMetadata.class, key, updateExpression, expressionAttributeValues); } catch (Exception e) { String errorMessage = String.format("Failed to append to key3 in DDB, primaryKey: %s, secondaryKey: %s. Reason: %s", primaryKeyValue, secondaryKeyValue, e.getMessage()); log.error(errorMessage, e); throw new DependencyException(errorMessage, e); } }
关于APPEND_SET的说明
APPEND_SET是DynamoDBMapper的SaveBehavior枚举值,仅适用于Set类型属性(如StringSet),作用是将新元素追加到现有Set中(自动去重)。而你需要的是List类型(允许重复、保持顺序),因此APPEND_SET不适用当前场景。
为什么不用原save方法?
如果用原save方式,需要先查询出ExternalTestMetadata对象,获取原key3列表后添加新元素再保存。但这种方式存在并发风险:若两个请求同时查询、修改、保存,会导致其中一个请求的追加操作丢失。而UpdateExpression是服务器端原子操作,能避免这类问题。
内容的提问来源于stack exchange,提问作者Blake
相关产品推荐
相关产品推荐

