【问题标题】:DynamoDBMapper save item only if uniqueDynamoDBMapper 仅在唯一时保存项目
【发布时间】:2017-03-16 01:08:06
【问题描述】:

我正在尝试根据两个不同列的组合使表中的项目保持唯一。

我有一个 instanceId 和 imageId 列(以及其他列),并且基于 StackoverflowAWS Forums 上的几篇帖子,下面应该可以吗?

public void saveUnique(Server server) {
    DynamoDBSaveExpression saveExpression = new DynamoDBSaveExpression();
    Map<String, ExpectedAttributeValue> expectedAttributes =
        ImmutableMap.<String, ExpectedAttributeValue>builder()
            .put("instanceId", new ExpectedAttributeValue(false))
            .put("imageId", new ExpectedAttributeValue(false))
            .build();
    saveExpression.setExpected(expectedAttributes);
    saveExpression.setConditionalOperator(ConditionalOperator.AND);
    try {
        mapper.save(server, saveExpression);
    } catch (ConditionalCheckFailedException e) {
        //Handle conditional check
    }
}

但是,每次我尝试保存重复项(相同的 instanceId 和 imageId)时,它都会成功保存到数据库中。

我还有什么遗漏吗?

编辑 R.E notionquest 答案

更新到下面的答案。

我有一项工作,每分钟运行一次,轮询 API。 API 的响应表示为Server POJO。 Server 有一个名为 instanceId 的属性。

我想确保如果带有instanceIdServer 已经在数据库中,请不要保存它。

Server 对象有另一个属性id,它被设置为表主键。

public void saveUnique(Server server) {
    DynamoDBSaveExpression saveExpression = new DynamoDBSaveExpression();

    Map<String, ExpectedAttributeValue> expected = new HashMap<>();
    expected.put("instanceId", new ExpectedAttributeValue(new AttributeValue(server.getInstanceId())).withComparisonOperator(ComparisonOperator.NE));
    saveExpression.setExpected(expected);

    try {
        mapper.save(server, saveExpression);
    } catch (ConditionalCheckFailedException e) {
        LOGGER.info("Skipped saving as not unique...");
    }
}

此代码将一遍又一遍地保存 Server 对象,而不会引发异常。

服务器 POJO

@DynamoDBTable(tableName = "Servers")
public class Server {

    @Id
    private String id = UUID.randomUUID().toString();

    @DynamoDBTypeConvertedJson
    private Task task;

    @DynamoDBAttribute(attributeName = "instanceId")
    private String instanceId;

    public Server() {
    }

    @DynamoDBHashKey
    public String getId() {
        return id;
    }

    // other standard getters and setters
}

【问题讨论】:

  • 可能,它可能没有在数据库中保存任何内容。 catch 块应该被执行并且方法应该正常结束而没有任何错误。您是否检查了数据库对于相同的 instanceId 和 imageId 是否存在两个不同的项目。它们是否定义为表的分区键和排序键?
  • 我发现如果我的分区键设置为instanceId,我上面的代码就可以工作。但是,如果我将其他内容(如通用 ID)设置为分区键,则它不起作用。必须可以检查两个对象是否相等,而不必使用分区键,确定吗?

标签: java amazon-web-services amazon-dynamodb


【解决方案1】:

2019 年更新

在 2019 年,自提出问题以来,这里似乎没有任何变化。在 DynamoDB 中为非主键字段提供唯一性仍然很棘手。这是最近在亚马逊上发布的一篇文章:https://aws.amazon.com/blogs/database/simulating-amazon-dynamodb-unique-constraints-using-transactions/

基本上,作者建议在同一个表中创建辅助记录,而不是使用如下所述的附加表。

例如,在下图中,为确保 instance_id=2c5d0cc8d900 是唯一值,您必须添加 具有人工主键值“instance_id#2c5d0cc8d900”的记录。如果插入成功,您可以插入主记录本身。

+--------------------------+-------------------------+--------+----------------
|           id             |       instance_id       | attr1  | other fields... 
|                          |                         |        |
|      (primary key)       |     (a non-key field,   |        |
|                          |     must be unique)     |        |
+--------------------------+-------------------------+--------+----------------
| instance_id#2c5d0cc8d900 |                         |        |
| a9fd702a                 | 2c5d0cc8d900            | qwerty | ...

虽然这种方法可能效果很好,但我个人仍然更喜欢使用单独的表格,如下面的原始答案所述。因为,当从包含此类辅助记录的表中读取数据时,您必须注意从实际记录中过滤它们。


原答案

如果我正确理解了这个问题,您希望确保不是哈希键的字段的唯一性。

(我不确定您为什么不使用 instanceId 作为 Servers 表的哈希键,我想您有这样做的原因)。

我的回答:看起来如果不使用辅助表就无法做到这一点

这是您现有的服务器表:

+----------------------------------------------+
|                 Servers                      |   
+----------------------------------------------+
| * id            the hash key                 |
| * instanceId    non-key field, must be unique|
|                                              |
| * ...                                        |
| * other fields                               |
| * ...                                        | 
+----------------------------------------------+

我会创建一个带有 instanceId 作为哈希键的附加表:

+----------------------------------------------+
|                 Instance                     |   
+----------------------------------------------+
| * instanceId    the hash key                 |
+----------------------------------------------+

有了这样的表,在将记录保存到服务器之前,您必须 首先确保 instanceId 值是唯一的,方法是向 Instance 添加一条记录 (putItem),并提供像 attribute_not_exists(instanceId) 这样的 ConditionExpression。

只有当 put 操作完成且没有出现ConditionalCheckFailedException 错误时,您才能继续将记录添加到服务器中。

如果您想根据 instanceId 和 imageId 这两个字段的组合来确保服务器中记录的唯一性, 而不是 instanceId 使用这些字段的连接值作为辅助表中的单个字段:

+----------------------------------------------+
|               Instance_Image                 |   
+----------------------------------------------+
| * instanceId_imageId   the hash key          |
+----------------------------------------------+

【讨论】:

  • 不确定为什么会有这种限制。无论如何,如果它是主键,则无法使用相同的主键创建重复项..为什么要首先创建条件。第三张表太多了..为什么我想使用的任何字段都不只是 NE。我可以为这个文件创建一个索引。不限于仅使用主键..我的意思是。
【解决方案2】:

以下代码检查属性值是否不等于与“somevalue”,如果满足条件,则更新项目。

Map<String, ExpectedAttributeValue> expected = new HashMap<>();     
expected.put("yourattribute", new ExpectedAttributeValue(new AttributeValue("yourvalue")).withComparisonOperator(ComparisonOperator.NE));

【讨论】:

  • 不幸的是,这似乎也只有在 "yourattribute" 是主键时才有效。如果它是另一个随机属性,它仍然将其写入数据库。
  • 不,我已经用非关键属性进行了测试。 catch 块被执行。试试我给的。如果您只使用一个属性,请删除 AND 条件。如果它不起作用,请在没有太多抽象的情况下准确解释您要实现的目标。
  • 问题已更新,希望进一步澄清。
  • 按要求更新;
  • 对我不起作用。如果您的属性不是主键,仍然没有例外。创建:stackoverflow.com/questions/50005071/…
猜你喜欢
  • 1970-01-01
  • 2017-06-17
  • 1970-01-01
  • 2017-07-14
  • 2021-02-01
  • 2017-03-02
  • 2023-01-14
  • 2012-11-09
  • 2018-04-25
相关资源
最近更新 更多