本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
执行事务操作
DynamoDB 增强版API客户端提供了transactGetItems()
和方法。transactWriteItems()
for Java 的SDK事务方法在 DynamoDB 表中提供了原子性、一致性、隔离性和持久性 (ACID),可帮助您保持应用程序中的数据正确性。
transactGetItems()
示例
transactGetItems()
方法最多可接受 100 个单独的项目请求。所有项目都在单个原子事务中读取。《Amazon DynamoDB 开发人员指南》包含有关导致 transactGetItems()
方法失败的条件以及您调用 transactGetItem()
时使用的隔离级别的信息。
在以下示例的注释行 1 之后,代码使用 builder
参数调用 transactGetItems()
方法。生成器使用数据对象调用三次,该数据对象包含用于生成最终请求的键值。addGetItem()
SDK
该请求在注释行 2 之后返回 Document
对象列表。返回的文档列表包含项目数据的非空 DocumentDocument.getItem(MappedTableResource<T> mappedTableResource)
方法会将非类型化 Document
对象转换为类型化的 Java 对象,否则该方法返回 null。
public static void transactGetItemsExample(DynamoDbEnhancedClient enhancedClient, DynamoDbTable<ProductCatalog> catalogTable, DynamoDbTable<MovieActor> movieActorTable) { // 1. Request three items from two tables using a builder. final List<Document> documents = enhancedClient.transactGetItems(b -> b .addGetItem(catalogTable, Key.builder().partitionValue(2).sortValue("Title 55").build()) .addGetItem(movieActorTable, Key.builder().partitionValue("Sophie's Choice").sortValue("Meryl Streep").build()) .addGetItem(movieActorTable, Key.builder().partitionValue("Blue Jasmine").sortValue("Cate Blanchett").build()) .build()); // 2. A list of Document objects is returned in the same order as requested. ProductCatalog title55 = documents.get(0).getItem(catalogTable); if (title55 != null) { logger.info(title55.toString()); } MovieActor sophiesChoice = documents.get(1).getItem(movieActorTable); if (sophiesChoice != null) { logger.info(sophiesChoice.toString()); } // 3. The getItem() method returns null if the Document object contains no item from DynamoDB. MovieActor blueJasmine = documents.get(2).getItem(movieActorTable); if (blueJasmine != null) { logger.info(blueJasmine.toString()); } }
在代码示例运行之前,DynamoDB 表包含以下项目。
ProductCatalog{id=2, title='Title 55', isbn='orig_isbn', authors=[b, g], price=10}
MovieActor{movieName='Sophie's Choice', actorName='Meryl Streep', actingAward='Best Actress', actingYear=1982, actingSchoolName='Yale School of Drama'}
记录了以下输出。如果请求了某个项目但未找到,则该项目不会返回(像请求名为 Blue Jasmine
的电影那样)。
ProductCatalog{id=2, title='Title 55', isbn='orig_isbn', authors=[b, g], price=10}
MovieActor{movieName='Sophie's Choice', actorName='Meryl Streep', actingAward='Best Actress', actingYear=1982, actingSchoolName='Yale School of Drama'}
transactWriteItems()
示例
transactWriteItems()
在跨多个表的单个原子事务中最多接受 100 个放置、更新或删除操作。《Amazon DynamoDB 开发人员指南》包含有关底层 DynamoDB 服务操作的限制和失败条件的详细信息。
基本示例
在以下示例中,请求对两个表执行四个操作。之前已显示了相应的模型类 ProductCatalog 和 MovieActor。
三种可能的操作(放置、更新和删除)均使用专用的请求参数来指定详细信息。
注释行 1 之后的代码显示了 addPutItem()
方法的简单变体。该方法接受要放置的 MappedTableResource
对象和数据对象实例。注释行 2 之后的语句显示了接受 TransactPutItemEnhancedRequest
实例的变体。此变体允许您在请求中添加更多选项,例如条件表达式。随后的示例显示了单个操作的条件表达式。
在注释行 3 之后请求更新操作。 TransactUpdateItemEnhancedRequest
有一种ignoreNulls()
方法可以让你配置SDK如何处理模型对象上的null
值。如果该ignoreNulls()
方法返回 true,则SDK不会移除数据对象属性的表属性值null
。如果该ignoreNulls()
方法返回 false,则会SDK请求 DynamoDB 服务从表中的项目中移除属性。ignoreNulls
的默认值为 false。
注释行 4 之后的语句显示了接受数据对象的删除请求的变体。增强型客户端在分派最终请求之前提取键值。
public static void transactWriteItems(DynamoDbEnhancedClient enhancedClient, DynamoDbTable<ProductCatalog> catalogTable, DynamoDbTable<MovieActor> movieActorTable) { enhancedClient.transactWriteItems(b -> b // 1. Simplest variation of put item request. .addPutItem(catalogTable, getProductCatId2()) // 2. Put item request variation that accommodates condition expressions. .addPutItem(movieActorTable, TransactPutItemEnhancedRequest.builder(MovieActor.class) .item(getMovieActorStreep()) .conditionExpression(Expression.builder().expression("attribute_not_exists (movie)").build()) .build()) // 3. Update request that does not remove attribute values on the table if the data object's value is null. .addUpdateItem(catalogTable, TransactUpdateItemEnhancedRequest.builder(ProductCatalog.class) .item(getProductCatId4ForUpdate()) .ignoreNulls(Boolean.TRUE) .build()) // 4. Variation of delete request that accepts a data object. The key values are extracted for the request. .addDeleteItem(movieActorTable, getMovieActorBlanchett()) ); }
以下帮助程序方法为 add*Item
参数提供了数据对象。
public static ProductCatalog getProductCatId2() { return ProductCatalog.builder() .id(2) .isbn("1-565-85698") .authors(new HashSet<>(Arrays.asList("a", "b"))) .price(BigDecimal.valueOf(30.22)) .title("Title 55") .build(); } public static ProductCatalog getProductCatId4ForUpdate() { return ProductCatalog.builder() .id(4) .price(BigDecimal.valueOf(40.00)) .title("Title 1") .build(); } public static MovieActor getMovieActorBlanchett() { MovieActor movieActor = new MovieActor(); movieActor.setActorName("Cate Blanchett"); movieActor.setMovieName("Tar"); movieActor.setActingYear(2022); movieActor.setActingAward("Best Actress"); movieActor.setActingSchoolName("National Institute of Dramatic Art"); return movieActor; } public static MovieActor getMovieActorStreep() { MovieActor movieActor = new MovieActor(); movieActor.setActorName("Meryl Streep"); movieActor.setMovieName("Sophie's Choice"); movieActor.setActingYear(1982); movieActor.setActingAward("Best Actress"); movieActor.setActingSchoolName("Yale School of Drama"); return movieActor; }
在代码示例运行之前,DynamoDB 表包含以下项目。
1 | ProductCatalog{id=4, title='Title 1', isbn='orig_isbn', authors=[b, g], price=10}
2 | MovieActor{movieName='Tar', actorName='Cate Blanchett', actingAward='Best Actress', actingYear=2022, actingSchoolName='National Institute of Dramatic Art'}
代码运行完毕后,表中将显示以下项目。
3 | ProductCatalog{id=2, title='Title 55', isbn='1-565-85698', authors=[a, b], price=30.22}
4 | ProductCatalog{id=4, title='Title 1', isbn='orig_isbn', authors=[b, g], price=40.0}
5 | MovieActor{movieName='Sophie's Choice', actorName='Meryl Streep', actingAward='Best Actress', actingYear=1982, actingSchoolName='Yale School of Drama'}
第 2 行的项目已被删除,第 3 行和第 5 行显示了已放置的项目。第 4 行显示第 1 行的更新。price
值是该项目上唯一更改的值。如果 ignoreNulls()
返回 false,第 4 行将类似于以下行。
ProductCatalog{id=4, title='Title 1', isbn='null', authors=null, price=40.0}
条件检查示例
以下示例演示条件检查的使用。条件检查用于检查项目是否存在,或者检查数据库中项目特定属性的条件。条件检查中检查的项目不能用于事务中的其他操作。
注意
您不能将同一个事务中的多个操作指向同一个项目。例如,您不能在同一个事务中对相同项目既执行条件检查又执行更新操作。
该示例演示事务写入项目请求中的每种操作类型。在注释行 2 之后,如果 conditionExpression
参数的计算结果为 false
,则 addConditionCheck()
方法会提供事务失败的条件。从帮助程序方法块中显示的方法返回的条件表达式会检查电影 Sophie's
Choice
的获奖年份是否不等于 1982
。如果是,则表达式的计算结果为 false
,事务将失败。
本指南的另一个主题深入讨论了表达式。
public static void conditionCheckFailExample(DynamoDbEnhancedClient enhancedClient, DynamoDbTable<ProductCatalog> catalogTable, DynamoDbTable<MovieActor> movieActorTable) { try { enhancedClient.transactWriteItems(b -> b // 1. Perform one of each type of operation with the next three methods. .addPutItem(catalogTable, TransactPutItemEnhancedRequest.builder(ProductCatalog.class) .item(getProductCatId2()).build()) .addUpdateItem(catalogTable, TransactUpdateItemEnhancedRequest.builder(ProductCatalog.class) .item(getProductCatId4ForUpdate()) .ignoreNulls(Boolean.TRUE).build()) .addDeleteItem(movieActorTable, TransactDeleteItemEnhancedRequest.builder() .key(b1 -> b1 .partitionValue(getMovieActorBlanchett().getMovieName()) .sortValue(getMovieActorBlanchett().getActorName())).build()) // 2. Add a condition check on a table item that is not involved in another operation in this request. .addConditionCheck(movieActorTable, ConditionCheck.builder() .conditionExpression(buildConditionCheckExpression()) .key(k -> k .partitionValue("Sophie's Choice") .sortValue("Meryl Streep")) // 3. Specify the request to return existing values from the item if the condition evaluates to true. .returnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure.ALL_OLD) .build()) .build()); // 4. Catch the exception if the transaction fails and log the information. } catch (TransactionCanceledException ex) { ex.cancellationReasons().stream().forEach(cancellationReason -> { logger.info(cancellationReason.toString()); }); } }
前面的代码示例中使用了以下帮助程序方法。
private static Expression buildConditionCheckExpression() { Map<String, AttributeValue> expressionValue = Map.of( ":year", numberValue(1982)); return Expression.builder() .expression("actingyear <> :year") .expressionValues(expressionValue) .build(); } public static ProductCatalog getProductCatId2() { return ProductCatalog.builder() .id(2) .isbn("1-565-85698") .authors(new HashSet<>(Arrays.asList("a", "b"))) .price(BigDecimal.valueOf(30.22)) .title("Title 55") .build(); } public static ProductCatalog getProductCatId4ForUpdate() { return ProductCatalog.builder() .id(4) .price(BigDecimal.valueOf(40.00)) .title("Title 1") .build(); } public static MovieActor getMovieActorBlanchett() { MovieActor movieActor = new MovieActor(); movieActor.setActorName("Cate Blanchett"); movieActor.setMovieName("Blue Jasmine"); movieActor.setActingYear(2013); movieActor.setActingAward("Best Actress"); movieActor.setActingSchoolName("National Institute of Dramatic Art"); return movieActor; }
在代码示例运行之前,DynamoDB 表包含以下项目。
1 | ProductCatalog{id=4, title='Title 1', isbn='orig_isbn', authors=[b, g], price=10}
2 | MovieActor{movieName='Sophie's Choice', actorName='Meryl Streep', actingAward='Best Actress', actingYear=1982, actingSchoolName='Yale School of Drama'}
3 | MovieActor{movieName='Tar', actorName='Cate Blanchett', actingAward='Best Actress', actingYear=2022, actingSchoolName='National Institute of Dramatic Art'}
代码运行完毕后,表中将显示以下项目。
ProductCatalog{id=4, title='Title 1', isbn='orig_isbn', authors=[b, g], price=10}
MovieActor{movieName='Sophie's Choice', actorName='Meryl Streep', actingAward='Best Actress', actingYear=1982, actingSchoolName='Yale School of Drama'}
MovieActor{movieName='Tar', actorName='Cate Blanchett', actingAward='Best Actress', actingYear=2022, actingSchoolName='National Institute of Dramatic Art'}
由于事务失败,表中的项目保持不变。影片 Sophie's Choice
的 actingYear
值为 1982
,如调用 transactWriteItem()
方法之前表中项目的第 2 行所示。
要捕获事务的取消信息,请将 transactWriteItems()
方法调用封装在一个 try
块中,然后 catch
TransactionCanceledException
CancellationReason
对象。由于示例注释行 3 之后的代码指定应返回导致事务失败的项目的值,因此日志会显示电影项目 Sophie's
Choice
的原始数据库值。
CancellationReason(Code=None) CancellationReason(Code=None) CancellationReason(Code=None) CancellationReason(Item={actor=AttributeValue(S=Meryl Streep), movie=AttributeValue(S=Sophie's Choice), actingaward=AttributeValue(S=Best Actress), actingyear=AttributeValue(N=1982), actingschoolname=AttributeValue(S=Yale School of Drama)}, ¬ Code=ConditionalCheckFailed, Message=The conditional request failed.)
单一操作条件示例
以下示例演示在事务请求中对单个操作使用条件的情况。注释行 1 之后的删除操作包含一个条件,该条件用于根据数据库检查操作的目标项目的值。在此示例中,在注释行 2 之后使用帮助程序方法创建的条件表达式指定,如果电影的上映年份不等于 2013 年,则应从数据库中删除该项目。
本指南稍后将讨论表达式。
public static void singleOperationConditionFailExample(DynamoDbEnhancedClient enhancedClient, DynamoDbTable<ProductCatalog> catalogTable, DynamoDbTable<MovieActor> movieActorTable) { try { enhancedClient.transactWriteItems(b -> b .addPutItem(catalogTable, TransactPutItemEnhancedRequest.builder(ProductCatalog.class) .item(getProductCatId2()) .build()) .addUpdateItem(catalogTable, TransactUpdateItemEnhancedRequest.builder(ProductCatalog.class) .item(getProductCatId4ForUpdate()) .ignoreNulls(Boolean.TRUE).build()) // 1. Delete operation that contains a condition expression .addDeleteItem(movieActorTable, TransactDeleteItemEnhancedRequest.builder() .key((Key.Builder k) -> { MovieActor blanchett = getMovieActorBlanchett(); k.partitionValue(blanchett.getMovieName()) .sortValue(blanchett.getActorName()); }) .conditionExpression(buildDeleteItemExpression()) .returnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure.ALL_OLD) .build()) .build()); } catch (TransactionCanceledException ex) { ex.cancellationReasons().forEach(cancellationReason -> logger.info(cancellationReason.toString())); } } // 2. Provide condition expression to check if 'actingyear' is not equal to 2013. private static Expression buildDeleteItemExpression() { Map<String, AttributeValue> expressionValue = Map.of( ":year", numberValue(2013)); return Expression.builder() .expression("actingyear <> :year") .expressionValues(expressionValue) .build(); }
前面的代码示例中使用了以下帮助程序方法。
public static ProductCatalog getProductCatId2() { return ProductCatalog.builder() .id(2) .isbn("1-565-85698") .authors(new HashSet<>(Arrays.asList("a", "b"))) .price(BigDecimal.valueOf(30.22)) .title("Title 55") .build(); } public static ProductCatalog getProductCatId4ForUpdate() { return ProductCatalog.builder() .id(4) .price(BigDecimal.valueOf(40.00)) .title("Title 1") .build(); } public static MovieActor getMovieActorBlanchett() { MovieActor movieActor = new MovieActor(); movieActor.setActorName("Cate Blanchett"); movieActor.setMovieName("Blue Jasmine"); movieActor.setActingYear(2013); movieActor.setActingAward("Best Actress"); movieActor.setActingSchoolName("National Institute of Dramatic Art"); return movieActor; }
在代码示例运行之前,DynamoDB 表包含以下项目。
1 | ProductCatalog{id=4, title='Title 1', isbn='orig_isbn', authors=[b, g], price=10}
2 | MovieActor{movieName='Blue Jasmine', actorName='Cate Blanchett', actingAward='Best Actress', actingYear=2013, actingSchoolName='National Institute of Dramatic Art'}
代码运行完毕后,表中将显示以下项目。
ProductCatalog{id=4, title='Title 1', isbn='orig_isbn', authors=[b, g], price=10}
2023-03-15 11:29:07 [main] INFO org.example.tests.TransactDemoTest:168 - MovieActor{movieName='Blue Jasmine', actorName='Cate Blanchett', actingAward='Best Actress', actingYear=2013, actingSchoolName='National Institute of Dramatic Art'}
由于事务失败,表中的项目保持不变。在代码示例运行之前,影片 Blue Jasmine
的 actingYear
值为 2013
,如项目列表第 2 行所示。
以下行记录到控制台。
CancellationReason(Code=None) CancellationReason(Code=None) CancellationReason(Item={actor=AttributeValue(S=Cate Blanchett), movie=AttributeValue(S=Blue Jasmine), actingaward=AttributeValue(S=Best Actress), actingyear=AttributeValue(N=2013), actingschoolname=AttributeValue(S=National Institute of Dramatic Art)}, Code=ConditionalCheckFailed, Message=The conditional request failed)