处理项目:Java - Amazon DynamoDB

处理项目:Java

您可以使用 AWS SDK for Java 文档 API 对某个表中的 Amazon DynamoDB 项目执行典型的创建、读取、更新和删除 (CRUD) 操作。

注意

还提供一个对象持久化模型,可用来将客户端类映射到 DynamoDB 表。该方法可以减少需要编写的代码数量。有关更多信息,请参阅 Java 1.x:DynamoDBMapper

此部分包含执行多个 Java 文档 API 项目操作的 Java 示例和多个可完全工作的示例。

放置项目

putItem 方法能将项目存储在表中。如果项目已存在,则会替换整个项目。如果您不想替换整个项目,而只希望更新特定属性,那么您可以使用 updateItem 方法。有关更多信息,请参阅 更新项目

Java v2
import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; import software.amazon.awssdk.services.dynamodb.model.AttributeValue; import software.amazon.awssdk.services.dynamodb.model.DynamoDbException; import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; import software.amazon.awssdk.services.dynamodb.model.PutItemResponse; import software.amazon.awssdk.services.dynamodb.model.ResourceNotFoundException; import java.util.HashMap; /** * Before running this Java V2 code example, set up your development * environment, including your credentials. * * For more information, see the following documentation topic: * * https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html * * To place items into an Amazon DynamoDB table using the AWS SDK for Java V2, * its better practice to use the * Enhanced Client. See the EnhancedPutItem example. */ public class PutItem { public static void main(String[] args) { final String usage = """ Usage: <tableName> <key> <keyVal> <albumtitle> <albumtitleval> <awards> <awardsval> <Songtitle> <songtitleval> Where: tableName - The Amazon DynamoDB table in which an item is placed (for example, Music3). key - The key used in the Amazon DynamoDB table (for example, Artist). keyval - The key value that represents the item to get (for example, Famous Band). albumTitle - The Album title (for example, AlbumTitle). AlbumTitleValue - The name of the album (for example, Songs About Life ). Awards - The awards column (for example, Awards). AwardVal - The value of the awards (for example, 10). SongTitle - The song title (for example, SongTitle). SongTitleVal - The value of the song title (for example, Happy Day). **Warning** This program will place an item that you specify into a table! """; if (args.length != 9) { System.out.println(usage); System.exit(1); } String tableName = args[0]; String key = args[1]; String keyVal = args[2]; String albumTitle = args[3]; String albumTitleValue = args[4]; String awards = args[5]; String awardVal = args[6]; String songTitle = args[7]; String songTitleVal = args[8]; Region region = Region.US_EAST_1; DynamoDbClient ddb = DynamoDbClient.builder() .region(region) .build(); putItemInTable(ddb, tableName, key, keyVal, albumTitle, albumTitleValue, awards, awardVal, songTitle, songTitleVal); System.out.println("Done!"); ddb.close(); } public static void putItemInTable(DynamoDbClient ddb, String tableName, String key, String keyVal, String albumTitle, String albumTitleValue, String awards, String awardVal, String songTitle, String songTitleVal) { HashMap<String, AttributeValue> itemValues = new HashMap<>(); itemValues.put(key, AttributeValue.builder().s(keyVal).build()); itemValues.put(songTitle, AttributeValue.builder().s(songTitleVal).build()); itemValues.put(albumTitle, AttributeValue.builder().s(albumTitleValue).build()); itemValues.put(awards, AttributeValue.builder().s(awardVal).build()); PutItemRequest request = PutItemRequest.builder() .tableName(tableName) .item(itemValues) .build(); try { PutItemResponse response = ddb.putItem(request); System.out.println(tableName + " was successfully updated. The request id is " + response.responseMetadata().requestId()); } catch (ResourceNotFoundException e) { System.err.format("Error: The Amazon DynamoDB table \"%s\" can't be found.\n", tableName); System.err.println("Be sure that it exists and that you've typed its name correctly!"); System.exit(1); } catch (DynamoDbException e) { System.err.println(e.getMessage()); System.exit(1); } } }
Java v1

按照以下步骤进行操作:

  1. 创建 DynamoDB 类的实例。

  2. 创建 Table 类的实例来代表要处理的表。

  3. 创建 Item 类的实例来代表新项目。必须指定新项目的主键及其属性。

  4. 使用您在之前步骤中创建的 putItem,调用 Table 对象的 Item 方法。

以下 Java 代码示例演示了上述任务。代码将新项目写入 ProductCatalog 表。

AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build(); DynamoDB dynamoDB = new DynamoDB(client); Table table = dynamoDB.getTable("ProductCatalog"); // Build a list of related items List<Number> relatedItems = new ArrayList<Number>(); relatedItems.add(341); relatedItems.add(472); relatedItems.add(649); //Build a map of product pictures Map<String, String> pictures = new HashMap<String, String>(); pictures.put("FrontView", "http://example.com/products/123_front.jpg"); pictures.put("RearView", "http://example.com/products/123_rear.jpg"); pictures.put("SideView", "http://example.com/products/123_left_side.jpg"); //Build a map of product reviews Map<String, List<String>> reviews = new HashMap<String, List<String>>(); List<String> fiveStarReviews = new ArrayList<String>(); fiveStarReviews.add("Excellent! Can't recommend it highly enough! Buy it!"); fiveStarReviews.add("Do yourself a favor and buy this"); reviews.put("FiveStar", fiveStarReviews); List<String> oneStarReviews = new ArrayList<String>(); oneStarReviews.add("Terrible product! Do not buy this."); reviews.put("OneStar", oneStarReviews); // Build the item Item item = new Item() .withPrimaryKey("Id", 123) .withString("Title", "Bicycle 123") .withString("Description", "123 description") .withString("BicycleType", "Hybrid") .withString("Brand", "Brand-Company C") .withNumber("Price", 500) .withStringSet("Color", new HashSet<String>(Arrays.asList("Red", "Black"))) .withString("ProductCategory", "Bicycle") .withBoolean("InStock", true) .withNull("QuantityOnHand") .withList("RelatedItems", relatedItems) .withMap("Pictures", pictures) .withMap("Reviews", reviews); // Write the item to the table PutItemOutcome outcome = table.putItem(item);

在上述示例中,项目具有标量(StringNumberBooleanNull)、集 (String Set) 和文档类型(ListMap)的属性。

指定可选参数

除了必需的参数之外,您还可以为 putItem 方法指定可选参数。例如,以下 Java 代码示例使用可选参数指定上传项目的条件。如果未满足指定条件,那么 AWS SDK for Java 会抛出 ConditionalCheckFailedException。该代码示例在 putItem 方法中指定了以下可选参数:

  • 定义请求条件的 ConditionExpression。该代码定义了一个条件,仅在具有同一主键的现有项目包含与特定值相等的 ISBN 属性时,才替换该项目。

  • 将在条件中使用的 ExpressionAttributeValues 的映射。在这种情况下,只需要一个替换:条件表达式中的占位符 :val 在运行时被替换为要检查的实际 ISBN 值。

以下示例使用这些可选参数添加一个新的图书项目。

Item item = new Item() .withPrimaryKey("Id", 104) .withString("Title", "Book 104 Title") .withString("ISBN", "444-4444444444") .withNumber("Price", 20) .withStringSet("Authors", new HashSet<String>(Arrays.asList("Author1", "Author2"))); Map<String, Object> expressionAttributeValues = new HashMap<String, Object>(); expressionAttributeValues.put(":val", "444-4444444444"); PutItemOutcome outcome = table.putItem( item, "ISBN = :val", // ConditionExpression parameter null, // ExpressionAttributeNames parameter - we're not using it for this example expressionAttributeValues);

PutItem 和 JSON 文档

您可以将 JSON 文档作为属性存储在 DynamoDB 表中。为此,请使用 ItemwithJSON 方法。此方法会解析 JSON 文档并将每个元素映射到本地 DynamoDB 数据类型。

假设您想要存储以下 JSON 文档,其中包含可履行特定产品订单的供应商。

{ "V01": { "Name": "Acme Books", "Offices": [ "Seattle" ] }, "V02": { "Name": "New Publishers, Inc.", "Offices": ["London", "New York" ] }, "V03": { "Name": "Better Buy Books", "Offices": [ "Tokyo", "Los Angeles", "Sydney" ] } }

您可以使用 withJSON 方法,将此项目存储在 ProductCatalog 表的名为 VendorInfoMap 属性中。以下 Java 代码示例演示了如何执行此操作。

// Convert the document into a String. Must escape all double-quotes. String vendorDocument = "{" + " \"V01\": {" + " \"Name\": \"Acme Books\"," + " \"Offices\": [ \"Seattle\" ]" + " }," + " \"V02\": {" + " \"Name\": \"New Publishers, Inc.\"," + " \"Offices\": [ \"London\", \"New York\"" + "]" + "}," + " \"V03\": {" + " \"Name\": \"Better Buy Books\"," + "\"Offices\": [ \"Tokyo\", \"Los Angeles\", \"Sydney\"" + " ]" + " }" + " }"; Item item = new Item() .withPrimaryKey("Id", 210) .withString("Title", "Book 210 Title") .withString("ISBN", "210-2102102102") .withNumber("Price", 30) .withJSON("VendorInfo", vendorDocument); PutItemOutcome outcome = table.putItem(item);

获取项目

要检索单个项目,可以使用 getItem 对象的 Table 方法。按照以下步骤进行操作:

  1. 创建 DynamoDB 类的实例。

  2. 创建 Table 类的实例来代表要处理的表。

  3. 调用 getItem 实例的 Table 方法。您必须指定要检索的项目的主键。

以下 Java 代码示例演示了上述步骤。该代码获取具有指定分区键的项目。

AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build(); DynamoDB dynamoDB = new DynamoDB(client); Table table = dynamoDB.getTable("ProductCatalog"); Item item = table.getItem("Id", 210);

指定可选参数

除了必需的参数之外,您还可以为 getItem 方法指定可选参数。例如,以下 Java 代码示例使用可选方法仅检索属性的特定列表,并指定强一致性读取。(要了解有关读取一致性的更多信息,请参阅 DynamoDB 读取一致性。)

您可以使用 ProjectionExpression 只检索特定属性或元素,而不是整个项目。ProjectionExpression 可以使用文档路径指定顶级或嵌套属性。有关更多信息,请参阅 在 DynamoDB 中使用投影表达式

getItem 方法的参数不让您指定读取一致性。但是,您可以创建 GetItemSpec,这会将对所有输入的完整访问权限提供给低级 GetItem 操作。下面的代码示例创建 GetItemSpec,并且使用该规范作为 getItem 方法的输入。

GetItemSpec spec = new GetItemSpec() .withPrimaryKey("Id", 206) .withProjectionExpression("Id, Title, RelatedItems[0], Reviews.FiveStar") .withConsistentRead(true); Item item = table.getItem(spec); System.out.println(item.toJSONPretty());

要以人类可读格式输出 Item,请使用 toJSONPretty 方法。上一例中的输出类似于下文所示。

{ "RelatedItems" : [ 341 ], "Reviews" : { "FiveStar" : [ "Excellent! Can't recommend it highly enough! Buy it!", "Do yourself a favor and buy this" ] }, "Id" : 123, "Title" : "20-Bicycle 123" }

GetItem 和 JSON 文档

PutItem 和 JSON 文档 部分中,您在名为 VendorInfoMap 属性中存储了一个 JSON 文档。您可以使用 getItem 方法检索 JSON 格式的整个文档。或者,您可以使用文档路径表示来仅检索文档中的部分元素。以下 Java 代码示例演示了这些技术。

GetItemSpec spec = new GetItemSpec() .withPrimaryKey("Id", 210); System.out.println("All vendor info:"); spec.withProjectionExpression("VendorInfo"); System.out.println(table.getItem(spec).toJSON()); System.out.println("A single vendor:"); spec.withProjectionExpression("VendorInfo.V03"); System.out.println(table.getItem(spec).toJSON()); System.out.println("First office location for this vendor:"); spec.withProjectionExpression("VendorInfo.V03.Offices[0]"); System.out.println(table.getItem(spec).toJSON());

上一例中的输出类似于下文所示。

All vendor info: {"VendorInfo":{"V03":{"Name":"Better Buy Books","Offices":["Tokyo","Los Angeles","Sydney"]},"V02":{"Name":"New Publishers, Inc.","Offices":["London","New York"]},"V01":{"Name":"Acme Books","Offices":["Seattle"]}}} A single vendor: {"VendorInfo":{"V03":{"Name":"Better Buy Books","Offices":["Tokyo","Los Angeles","Sydney"]}}} First office location for a single vendor: {"VendorInfo":{"V03":{"Offices":["Tokyo"]}}}
注意

您可以使用 toJSON 方法将任意项目 (或其属性) 转换成 JSON 格式的字符串。以下代码检索多个顶级和嵌套属性,并以 JSON 格式输出结果。

GetItemSpec spec = new GetItemSpec() .withPrimaryKey("Id", 210) .withProjectionExpression("VendorInfo.V01, Title, Price"); Item item = table.getItem(spec); System.out.println(item.toJSON());

输出如下所示。

{"VendorInfo":{"V01":{"Name":"Acme Books","Offices":["Seattle"]}},"Price":30,"Title":"Book 210 Title"}

批处理写入:放置和删除多个项目

批量写入 是指批量放置和删除多个项目。batchWriteItem 方法可让您通过一次 调用即可向一个或多个表中放置或从中删除多个项目。下面是使用 AWS SDK for Java 文档 API 放置或删除多个项目的步骤。

  1. 创建 DynamoDB 类的实例。

  2. 创建描述表的所有放置和删除操作的 TableWriteItems 类的实例。如果要在单个批量写入操作中写入到多个表,您必须为每个表创建一个 TableWriteItems 实例。

  3. 通过提供您在之前步骤中创建的 batchWriteItem 对象,调用 TableWriteItems 方法。

  4. 处理响应。您应该检查一下响应是否返回未处理的请求项目。如果达到预置吞吐量配额或发生其他临时错误,就可能会出现这种情况。此外,DynamoDB 还对可在请求中指定的请求大小和操作数进行限制。如果超出这些限制,DynamoDB 会拒绝请求。有关更多信息,请参阅 Amazon DynamoDB 中的服务、账户和表限额

以下 Java 代码示例演示了上述步骤。本示例对两个表执行 batchWriteItem 操作:ForumThread。相应的 TableWriteItems 对象定义以下操作:

  • Forum 表中放置一个项目。

  • Thread 表放置和删除项目。

然后,代码调用 batchWriteItem 来执行操作。

AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build(); DynamoDB dynamoDB = new DynamoDB(client); TableWriteItems forumTableWriteItems = new TableWriteItems("Forum") .withItemsToPut( new Item() .withPrimaryKey("Name", "Amazon RDS") .withNumber("Threads", 0)); TableWriteItems threadTableWriteItems = new TableWriteItems("Thread") .withItemsToPut( new Item() .withPrimaryKey("ForumName","Amazon RDS","Subject","Amazon RDS Thread 1") .withHashAndRangeKeysToDelete("ForumName","Some partition key value", "Amazon S3", "Some sort key value"); BatchWriteItemOutcome outcome = dynamoDB.batchWriteItem(forumTableWriteItems, threadTableWriteItems); // Code for checking unprocessed items is omitted in this example

要了解可工作的示例,请参阅 示例:使用 AWS SDK for Java 文档 API 的批处理写入操作

批处理获取:获取多个项目

batchGetItem 方法可让您检索一个或多个表中的多个项目。要检索单个项目,您可以使用 getItem 方法。

按照以下步骤进行操作:

  1. 创建 DynamoDB 类的实例。

  2. 创建描述要从表中检索的主键值列表的 TableKeysAndAttributes 类的实例。如果需要在单个批量获取操作中读取多个表,需要为每个表创建一个 TableKeysAndAttributes 实例。

  3. 通过提供您在之前步骤中创建的 batchGetItem 对象,调用 TableKeysAndAttributes 方法。

以下 Java 代码示例演示了上述步骤。示例检索 Forum 表中的两个项目和 Thread 表中的三个项目。

AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build(); DynamoDB dynamoDB = new DynamoDB(client); TableKeysAndAttributes forumTableKeysAndAttributes = new TableKeysAndAttributes(forumTableName); forumTableKeysAndAttributes.addHashOnlyPrimaryKeys("Name", "Amazon S3", "Amazon DynamoDB"); TableKeysAndAttributes threadTableKeysAndAttributes = new TableKeysAndAttributes(threadTableName); threadTableKeysAndAttributes.addHashAndRangePrimaryKeys("ForumName", "Subject", "Amazon DynamoDB","DynamoDB Thread 1", "Amazon DynamoDB","DynamoDB Thread 2", "Amazon S3","S3 Thread 1"); BatchGetItemOutcome outcome = dynamoDB.batchGetItem( forumTableKeysAndAttributes, threadTableKeysAndAttributes); for (String tableName : outcome.getTableItems().keySet()) { System.out.println("Items in table " + tableName); List<Item> items = outcome.getTableItems().get(tableName); for (Item item : items) { System.out.println(item); } }

指定可选参数

在使用 batchGetItem 时,除了必需的参数之外,您还可以指定可选参数。例如,可以随您定义的每个 ProjectionExpression 提供一个 TableKeysAndAttributes。这样您可以指定要从表中检索的属性。

以下 C# 代码示例从 Forum 表检索两个项目。withProjectionExpression 参数指定仅检索 Threads 属性。

TableKeysAndAttributes forumTableKeysAndAttributes = new TableKeysAndAttributes("Forum") .withProjectionExpression("Threads"); forumTableKeysAndAttributes.addHashOnlyPrimaryKeys("Name", "Amazon S3", "Amazon DynamoDB"); BatchGetItemOutcome outcome = dynamoDB.batchGetItem(forumTableKeysAndAttributes);

更新项目

updateItem 对象的 Table 方法可以更新现有属性值,添加新属性,或者从现有项目中删除属性。

updateItem 方法的运行机制如下:

  • 如果项目不存在(表中不存在具有指定主键的项目),updateItem 会将新项目添加到表中

  • 如果项目存在,updateItem 会按照 UpdateExpression 参数指定的方式执行更新。

注意

还可以使用 putItem 来“更新”项目。例如,如果您调用 putItem 向表中添加项目,但是已存在具有指定主键的项目,那么 putItem 将替换整个项目。如果现有项目中有属性,并且这些属性未在输入中指定,putItem 从项目中删除这些属性。

通常,在您希望修改任何项目属性时,我们建议您使用 updateItemupdateItem 方法仅修改您在输入中指定的项目属性,项目中的其他属性将保持不变。

按照以下步骤进行操作:

  1. 创建 Table 类的实例来代表要处理的表。

  2. 调用 updateTable 实例的 Table 方法。必须指定要检索的项目的主键,同时指定描述要修改的属性以及如何修改这些属性的 UpdateExpression

以下 Java 代码示例演示了上述任务。该代码更新 ProductCatalog 表中的书本项目。将新作者添加到 Authors 集中,并删除现有 ISBN 属性。另外还降低了价格 (-1)。

ExpressionAttributeValues 中使用 UpdateExpression 映射。在运行时,占位符 :val1:val2 将被替换为 AuthorsPrice 的实际值。

AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build(); DynamoDB dynamoDB = new DynamoDB(client); Table table = dynamoDB.getTable("ProductCatalog"); Map<String, String> expressionAttributeNames = new HashMap<String, String>(); expressionAttributeNames.put("#A", "Authors"); expressionAttributeNames.put("#P", "Price"); expressionAttributeNames.put("#I", "ISBN"); Map<String, Object> expressionAttributeValues = new HashMap<String, Object>(); expressionAttributeValues.put(":val1", new HashSet<String>(Arrays.asList("Author YY","Author ZZ"))); expressionAttributeValues.put(":val2", 1); //Price UpdateItemOutcome outcome = table.updateItem( "Id", // key attribute name 101, // key attribute value "add #A :val1 set #P = #P - :val2 remove #I", // UpdateExpression expressionAttributeNames, expressionAttributeValues);

指定可选参数

除了必需的参数之外,您还可以为 updateItem 方法指定可选参数,其中包括为了执行更新而必须满足的条件。如果未满足指定条件,那么 AWS SDK for Java 会抛出 ConditionalCheckFailedException。例如,以下 Java 代码示例有条件地将书本物品价格更新为 25。它指定 ConditionExpression,后者声明仅当现有价格为 20 时才应更新价格。

Table table = dynamoDB.getTable("ProductCatalog"); Map<String, String> expressionAttributeNames = new HashMap<String, String>(); expressionAttributeNames.put("#P", "Price"); Map<String, Object> expressionAttributeValues = new HashMap<String, Object>(); expressionAttributeValues.put(":val1", 25); // update Price to 25... expressionAttributeValues.put(":val2", 20); //...but only if existing Price is 20 UpdateItemOutcome outcome = table.updateItem( new PrimaryKey("Id",101), "set #P = :val1", // UpdateExpression "#P = :val2", // ConditionExpression expressionAttributeNames, expressionAttributeValues);

原子计数器

您可以使用 updateItem 实现原子计数器,并使用该计数器来递增或递减现有属性的值而不会干扰其他写入请求。要递增原子计数器,请将 UpdateExpressionset 操作结合使用,以便将数值添加到类型为 Number 的现有属性。

以下示例演示了这一用法,将 Quantity 属性递增 1。它还演示在 ExpressionAttributeNames 中使用的 UpdateExpression 参数。

Table table = dynamoDB.getTable("ProductCatalog"); Map<String,String> expressionAttributeNames = new HashMap<String,String>(); expressionAttributeNames.put("#p", "PageCount"); Map<String,Object> expressionAttributeValues = new HashMap<String,Object>(); expressionAttributeValues.put(":val", 1); UpdateItemOutcome outcome = table.updateItem( "Id", 121, "set #p = #p + :val", expressionAttributeNames, expressionAttributeValues);

删除项目

deleteItem 方法能删除表中的项目。您必须提供要删除的项目的主键。

按照以下步骤进行操作:

  1. 创建 DynamoDB 客户端的实例。

  2. 通过提供要删除的项目的键来调用 deleteItem 方法。

以下 Java 示例演示了这些任务。

AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build(); DynamoDB dynamoDB = new DynamoDB(client); Table table = dynamoDB.getTable("ProductCatalog"); DeleteItemOutcome outcome = table.deleteItem("Id", 101);

指定可选参数

您可以为 deleteItem 指定可选参数。例如,以下 Java 代码示例指定 ConditionExpression,声明只有当图书不再位于出版物中(InPublication 属性为 false)时,才能删除 ProductCatalog 中的图书项目。

Map<String,Object> expressionAttributeValues = new HashMap<String,Object>(); expressionAttributeValues.put(":val", false); DeleteItemOutcome outcome = table.deleteItem("Id",103, "InPublication = :val", null, // ExpressionAttributeNames - not used in this example expressionAttributeValues);