

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# DynamoDB 映射 API 的版本 1 和版本 2 之间的区别 适用于 Java 的 AWS SDK
<a name="ddb-mapping"></a>

在的版本 1 和版本 2 之间，DynamoDB APIs 映射发生了显著变化。 适用于 Java 的 AWS SDK在版本 1 中，您可以使用`DynamoDBMapper`来处理 Java POJOs。在版本 2 中使用 `DynamoDbEnhancedClient`，该方法具有更新的方法名称、增强的架构定义选项和改进的类型安全性。

关键差异包括：
+ 新的方法名称（例如 `getItem`，而不使用 `load`）
+ 显式表架构创建
+ 同步和异步操作的内置支持
+ 空字符串和配置的处理方式发生了变化

本节介绍映射 API 更改、注释差异、配置更新和迁移指导，以协助您从 v1 `DynamoDBMapper` 转变到 v2 `DynamoDbEnhancedClient`。

**Contents**
+ [适用于 Java 的 SDK 版本 1 升级到版本 2 时，映射库发生的高级别更改](dynamodb-mapping-high-level.md)
  + [导入依赖项差异](dynamodb-mapping-high-level.md#dynamodb-mapping-deps)
+ [适用于 Java 的 SDK 版本 1 和版本 2 之间的 DynamoDB APIs 映射发生了变化](dynamodb-mapping-api-changes.md)
  + [创建客户端](dynamodb-mapping-api-changes.md#dynamodb-mapping-api-changes-client)
  + [建立到 DynamoDB 表/索引的映射](dynamodb-mapping-api-changes.md#dynamodb-mapping-api-changes-mapping)
  + [表操作](dynamodb-mapping-api-changes.md#dynamodb-mapping-api-changes-tobleops)
  + [映射类和属性](dynamodb-mapping-api-changes.md#dynamodb-mapping-schemas)
    + [bean 注释](dynamodb-mapping-api-changes.md#dynamodb-mapping-schemas-annos)
    + [V2 附加注释](dynamodb-mapping-api-changes.md#dynamodb-mapping-schemas-annos-v2-addnl)
  + [配置](dynamodb-mapping-api-changes.md#dynamodb-mapping-configuration)
    + [每个操作的配置](dynamodb-mapping-api-changes.md#dynamodb-mapping-configuration-per-op)
  + [条件](dynamodb-mapping-api-changes.md#dynamodb-mapping-conditionals)
  + [类型转换](dynamodb-mapping-api-changes.md#dynamodb-mapping-type-conv)
    + [默认转换器](dynamodb-mapping-api-changes.md#dynamodb-mapping-type-conv-defaults)
    + [为属性设置自定义转换器](dynamodb-mapping-api-changes.md#dynamodb-mapping-type-conv-anno)
    + [添加类型转换器工厂或提供程序](dynamodb-mapping-api-changes.md#dynamodb-mapping-type-conv-factory)
+ [适用于 Java 的 SDK 版本 1 和版本 2 之间的字符串处理差异](dynamodb-migration-string-handling.md)
+ [适用于 Java 的 SDK 版本 1 和版本 2 之间的乐观锁差异](dynamodb-migrate-optimstic-locking.md)
+ [适用于 Java 的 SDK 版本 1 和版本 2 之间的 fluent setter 方法差异](dynamodb-migrate-fluent-setters.md)

# 适用于 Java 的 SDK 版本 1 升级到版本 2 时，映射库发生的高级别更改
<a name="dynamodb-mapping-high-level"></a>

在 V1 和 V2 中，每个库中映射客户端的名称不同：
+ V1-迪纳摩 DBMapper
+ V2 - DynamoDB 增强型客户端

您与这两个库的交互方式大致相同：实例化 a， mapper/client 然后为其提供一个 Java POJO，用于 APIs 读取这些项目并将其写入 DynamoDB 表。这两个库还为 POJO 的类提供了注释，以指导客户端如何处理 POJO。

迁移到 V2 时的显著差异包括：
+ V2 和 V1 为低级 DynamoDB 操作使用不同的方法名称。例如：    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/sdk-for-java/latest/developer-guide/dynamodb-mapping-high-level.html)
+ V2 提供了多种定义表架构和映射 POJOs 到表格的方法。您可以选择使用注释或使用生成器从代码中生成的架构。V2 还提供架构的可变和不可变版本。
+ 在 V2 中，您需要在最初几个步骤中明确地创建表架构；而在 V1 中，根据需要从注释类中推断出表架构。
+ V2 在增强型客户端 API 中包括[文档 API 客户端](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/document/EnhancedDocument.html)，而 V1 则使用[单独的 API](https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/dynamodbv2/document/DynamoDB.html)。
+ 在 V2 中，所有版本 APIs 均提供同步和异步版本。

有关 V2 增强型客户端的更多详细信息，请参阅本指南中的 [DynamoDB 映射部分](dynamodb-enhanced-client.md)。

## 导入依赖项差异
<a name="dynamodb-mapping-deps"></a>


| V1 | V2 | 
| --- | --- | 
|  <pre><dependencyManagement><br />  <dependencies><br />    <dependency><br />      <groupId>com.amazonaws</groupId><br />      <artifactId>aws-java-sdk-bom</artifactId><br />      <version>1.X.X</version><br />      <type>pom</type><br />      <scope>import</scope><br />    </dependency><br />  </dependencies><br /></dependencyManagement> <br /><br /><dependencies><br />  <dependency><br />    <groupId>com.amazonaws</groupId><br />    <artifactId>aws-java-sdk-dynamodb</artifactId><br />  </dependency><br /></dependencies></pre>  |  <pre><dependencyManagement><br />  <dependencies><br />    <dependency><br />      <groupId>software.amazon.awssdk</groupId><br />      <artifactId>bom</artifactId><br />      <version>2.X.X*</version><br />      <type>pom</type><br />      <scope>import</scope><br />    </dependency><br />  </dependencies><br /></dependencyManagement> <br /><br /><dependencies><br />  <dependency><br />    <groupId>software.amazon.awssdk</groupId><br />    <artifactId>dynamodb-enhanced</artifactId><br />  </dependency><br /></dependencies></pre>  | 

\$1 [最新版本](https://central.sonatype.com/artifact/software.amazon.awssdk/bom)。

在 V1 中，单个依赖项包括低级 DynamoDB API 和 API，而在 V2 中，您可以使用构件依赖项来访问 mapping/document API。`dynamodb-enhanced` mapping/document `dynamodb-enhanced` 模块包含低级 `dynamodb` 模块的传递依赖项。

# 适用于 Java 的 SDK 版本 1 和版本 2 之间的 DynamoDB APIs 映射发生了变化
<a name="dynamodb-mapping-api-changes"></a>

## 创建客户端
<a name="dynamodb-mapping-api-changes-client"></a>


****  

| 使用案例 | V1 | V2 | 
| --- | --- | --- | 
|   正常实例化  |  <pre>AmazonDynamoDB standardClient = AmazonDynamoDBClientBuilder.standard()<br />    .withCredentials(credentialsProvider)<br />    .withRegion(Regions.US_EAST_1)<br />    .build();<br />DynamoDBMapper mapper = new DynamoDBMapper(standardClient);</pre>  |  <pre>DynamoDbClient standardClient = DynamoDbClient.builder()<br />    .credentialsProvider(ProfileCredentialsProvider.create())<br />    .region(Region.US_EAST_1)<br />    .build();<br />DynamoDbEnhancedClient enhancedClient = DynamoDbEnhancedClient.builder()<br />    .dynamoDbClient(standardClient)<br />    .build();</pre>  | 
|   最小实例化  |  <pre>AmazonDynamoDB standardClient = AmazonDynamoDBClientBuilder.standard();<br />DynamoDBMapper mapper = new DynamoDBMapper(standardClient);</pre>  |  <pre>DynamoDbEnhancedClient enhancedClient = DynamoDbEnhancedClient.create();</pre>  | 
|   带属性转换器\$1  |  <pre>DynamoDBMapper mapper = new DynamoDBMapper(standardClient, <br />                        attributeTransformerInstance);</pre>  |  <pre>DynamoDbEnhancedClient enhancedClient = DynamoDbEnhancedClient.builder()<br />    .dynamoDbClient(standardClient)<br />    .extensions(extensionAInstance, extensionBInstance)<br />    .build();</pre>  | 

\$1V2 中的扩展与 V1 中的属性转换器大致对应。[使用扩展自定义 DynamoDB 增强型客户端操作](ddb-en-client-extensions.md) 部分包含有关 V2 中扩展的更多信息。

## 建立到 DynamoDB 表/索引的映射
<a name="dynamodb-mapping-api-changes-mapping"></a>

在 V1 中，通过 bean 注释指定 DynamoDB 表名。在 V2 中，使用一种工厂方法 `table()` 来生成代表远程 DynamoDB 表的 `DynamoDbTable` 实例。`table()` 方法的第一个参数是 DynamoDB 表名。


****  

| 使用案例 | V1 | V2 | 
| --- | --- | --- | 
|   将 Java POJO 类映射到 DynamoDB 表  |  <pre>@DynamoDBTable(tableName ="Customer")<br />public class Customer {<br />  ...<br />}</pre>  |  <pre>DynamoDbTable<Customer> customerTable = enhancedClient.table("Customer",<br />    TableSchema.fromBean(Customer.class));</pre>  | 
|   映射到 DynamoDB 二级索引  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/sdk-for-java/latest/developer-guide/dynamodb-mapping-api-changes.html) 《DynamoDB 开发人员指南》中讨论 [V1 `query` 方法](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBMapper.Methods.html#DynamoDBMapper.Methods.query)的部分展示了一个完整的示例。  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/sdk-for-java/latest/developer-guide/dynamodb-mapping-api-changes.html) 本指南中的 [使用二级索引](ddb-en-client-use-secindex.md) 部分提供了更多信息。  | 

## 表操作
<a name="dynamodb-mapping-api-changes-tobleops"></a>

本节介绍大多数标准用例中 V1 和 V2 之间存在差异的操作 APIs 。

在 V2 中，所有涉及单个表的操作都是在 `DynamoDbTable` 实例上调用，而不是在增强型客户端上调用。增强型客户端包含可以针对多个表的方法。

在下面名为*表操作*的表中，POJO 实例被称为 `item` 或称为某个具体类型（例如 `customer1`）。对于 V2 示例，名为 `table` 的实例是先前调用 `enhancedClient.table()` 的结果，该调用返回对 `DynamoDbTable` 实例的引用。

请注意，即使未显示，也可以使用 fluent 使用者模式调用大多数 V2 操作。例如，

```
Customer customer = table.getItem(r → r.key(key));
  or
Customer customer = table.getItem(r → r.key(k -> k.partitionValue("id").sortValue("email")))
```

对于 V1 操作，*表操作*（在下方）包含一些常用的表单，并未涵盖所有重载表单。例如，`load()` 方法有以下重载：

```
mapper.load(Customer.class, hashKey)
mapper.load(Customer.class, hashKey, rangeKey)
mapper.load(Customer.class, hashKey, config)
mapper.load(Customer.class, hashKey, rangeKey, config)
mapper.load(item)
mapper.load(item, config)
```

*表操作*（在下方）显示了常用的表单：

```
mapper.load(item)
mapper.load(item, config)
```


**表操作**  

| 使用案例 | V1 | V2 | 
| --- | --- | --- | 
|  将 Java POJO 写入 DynamoDB 表 **DynamoDB 操作**：`PutItem`，`UpdateItem`  |  <pre>mapper.save(item)<br />mapper.save(item, config)<br />mapper.save(item, saveExpression, config)</pre> 在 V1 中，`DynamoDBMapperConfig.SaveBehavior` 和注释决定了将调用哪个低级 DynamoDB 方法。通常，除非在使用 `SaveBehavior.CLOBBER` 和 `SaveBehavior.PUT`，否则会调用 `UpdateItem`。自动生成的密钥是一种特殊的使用案例，偶尔会同时使用 `PutItem` 和 `UpdateItem`。  |  <pre>table.putItem(putItemRequest)<br />table.putItem(item)<br />table.putItemWithResponse(item) //Returns metadata.<br /><br />updateItem(updateItemRequest)<br />table.updateItem(item)<br />table.updateItemWithResponse(item) //Returns metadata.</pre>  | 
|  从 DynamoDB 表中将项目读取到 Java POJO **DynamoDB 操作：**`GetItem`  |  <pre>mapper.load(item)<br />mapper.load(item, config)</pre>  |  <pre>table.getItem(getItemRequest)<br />table.getItem(item)<br />table.getItem(key)<br />table.getItemWithResponse(key) //Returns POJO with metadata.</pre>  | 
|  从 DynamoDB 表中删除项目 **DynamoDB 操作：**`DeleteItem`  |  <pre>mapper.delete(item, deleteExpression, config)</pre>  |  <pre>table.deleteItem(deleteItemRequest)<br />table.deleteItem(item)<br />table.deleteItem(key)</pre>  | 
|  查询 DynamoDB 表或二级索引并返回分页列表 **DynamoDB 操作：**`Query`  |  <pre>mapper.query(Customer.class, queryExpression)<br />mapper.query(Customer.class, queryExpression, <br />                             mapperConfig)</pre>  |  <pre>table.query(queryRequest)<br />table.query(queryConditional)</pre> 使用返回的 `PageIterable.stream()`（延迟加载）进行同步响应，使用 `PagePublisher.subscribe()` 进行异步响应  | 
|  查询 DynamoDB 表或二级索引并返回列表 **DynamoDB 操作：**`Query`  |  <pre>mapper.queryPage(Customer.class, queryExpression)<br />mapper.queryPage(Customer.class, queryExpression, <br />                                 mapperConfig)</pre>  |  <pre>table.query(queryRequest)<br />table.query(queryConditional)</pre> 使用返回的 `PageIterable.items()`（延迟加载）进行同步响应，使用 `PagePublisher.items.subscribe()` 进行异步响应  | 
|  扫描 DynamoDB 表或二级索引并返回分页列表 **DynamoDB 操作：**`Scan`  |  <pre>mapper.scan(Customer.class, scanExpression)<br />mapper.scan(Customer.class, scanExpression, <br />                            mapperConfig)</pre>  |  <pre>table.scan()<br />table.scan(scanRequest)</pre> 使用返回的 `PageIterable.stream()`（延迟加载）进行同步响应，使用 `PagePublisher.subscribe()` 进行异步响应  | 
|  扫描 DynamoDB 表或二级索引并返回列表 **DynamoDB 操作：**`Scan`  |  <pre>mapper.scanPage(Customer.class, scanExpression)<br />mapper.scanPage(Customer.class, scanExpression, <br />                                mapperConfig)</pre>  |  <pre>table.scan()<br />table.scan(scanRequest)</pre> 使用返回的 `PageIterable.items()`（延迟加载）进行同步响应，使用 `PagePublisher.items.subscribe()` 进行异步响应  | 
|  批量读取多个表中的多个项目 **DynamoDB 操作：**`BatchGetItem`  |  <pre>mapper.batchLoad(Arrays.asList(customer1, <br />                               customer2, <br />                               book1))<br />mapper.batchLoad(itemsToGet) <br />           // itemsToGet: Map<Class<?>, List<KeyPair>></pre>  |  <pre>enhancedClient.batchGetItem(batchGetItemRequest)<br /><br />enhancedClient.batchGetItem(r -> r.readBatches(<br />    ReadBatch.builder(Record1.class)<br />             .mappedTableResource(mappedTable1)<br />             .addGetItem(i -> i.key(k -> k.partitionValue(0)))<br />             .build(),<br />    ReadBatch.builder(Record2.class)<br />             .mappedTableResource(mappedTable2)<br />             .addGetItem(i -> i.key(k -> k.partitionValue(0)))<br />             .build()))<br /><br />// Iterate over pages with lazy loading or over all items <br />   from the same table.</pre>  | 
|  批量将多个项目写入多个表 **DynamoDB 操作：**`BatchWriteItem`  |  <pre>mapper.batchSave(Arrays.asList(customer1, <br />                               customer2, <br />                               book1)) </pre>  |  <pre>enhancedClient.batchWriteItem(batchWriteItemRequest)<br /><br />enhancedClient.batchWriteItem(r -> r.writeBatches(<br />    WriteBatch.builder(Record1.class)<br />             .mappedTableResource(mappedTable1)<br />             .addPutItem(item1)<br />             .build(),<br />    WriteBatch.builder(Record2.class)<br />             .mappedTableResource(mappedTable2)<br />             .addPutItem(item2)<br />             .build()))</pre>  | 
|  批量删除多个表中的多个项目 **DynamoDB 操作：**`BatchWriteItem`  |  <pre>mapper.batchDelete(Arrays.asList(customer1, <br />                                 customer2, <br />                                 book1)) </pre>  |  <pre>enhancedClient.batchWriteItem(r -> r.writeBatches(<br />    WriteBatch.builder(Record1.class)<br />             .mappedTableResource(mappedTable1)<br />             .addDeleteItem(item1key)<br />             .build(),<br />    WriteBatch.builder(Record2.class)<br />             .mappedTableResource(mappedTable2)<br />             .addDeleteItem(item2key)<br />             .build()))</pre>  | 
|  批量写入/删除多个项目 **DynamoDB 操作：**`BatchWriteItem`  |  <pre>mapper.batchWrite(Arrays.asList(customer1, book1), <br />                  Arrays.asList(customer2)) </pre>  |  <pre>enhancedClient.batchWriteItem(r -> r.writeBatches(<br />    WriteBatch.builder(Record1.class)<br />             .mappedTableResource(mappedTable1)<br />             .addPutItem(item1)<br />             .build(),<br />    WriteBatch.builder(Record2.class)<br />             .mappedTableResource(mappedTable2)<br />             .addDeleteItem(item2key)<br />             .build()))</pre>  | 
|  执行事务性写入 **DynamoDB 操作：**`TransactWriteItems`  |  <pre>mapper.transactionWrite(transactionWriteRequest)</pre>  |  <pre>enhancedClient.transactWriteItems(transasctWriteItemsRequest)</pre>  | 
|  执行事务性读取 **DynamoDB 操作：**`TransactGetItems`  |  <pre>mapper.transactionLoad(transactionLoadRequest)</pre>  |  <pre>enhancedClient.transactGetItems(transactGetItemsRequest) </pre>  | 
|  获取查询中匹配项的计数 **DynamoDB 操作：**使用 `Select.COUNT` 执行 `Query`  |  <pre>mapper.count(Customer.class, queryExpression)</pre>  |  <pre>// Get the count from query results.<br />PageIterable<Customer> pageIterable =<br />    customerTable.query(QueryEnhancedRequest.builder()<br />        .queryConditional(queryConditional)<br />        .select(Select.COUNT)<br />        .build());<br />Iterator<Page<Customer>> iterator = pageIterable.iterator();<br />Page<Customer> page = iterator.next();<br />int count = page.count();<br /><br />// For a more concise approach, you can chain the method calls:<br />int count = customerTable.query(QueryEnhancedRequest.builder()<br />                .queryConditional(queryConditional)<br />                .select(Select.COUNT)<br />                .build())<br />            .iterator().next().count();</pre>  | 
|  获取扫描中匹配项的计数 **DynamoDB 操作：**使用 `Select.COUNT` 执行 `Scan`  |  <pre>mapper.count(Customer.class, scanExpression)</pre>  |  <pre>// Get the count from scan results.<br />PageIterable<Customer> pageIterable =<br />    customerTable.scan(ScanEnhancedRequest.builder()<br />        .filterExpression(filterExpression)<br />        .select(Select.COUNT)<br />        .build());<br />Iterator<Page<Customer>> iterator = pageIterable.iterator();<br />Page<Customer> page = iterator.next();<br />int count = page.count();<br /><br />// For a more concise approach, you can chain the method calls:<br />int count = customerTable.scan(ScanEnhancedRequest.builder()<br />                .filterExpression(filterExpression)<br />                .select(Select.COUNT)<br />                .build())<br />            .iterator().next().count();</pre>  | 
|  在 DynamoDB 中创建与 POJO 类对应的表 **DynamoDB 操作：**`CreateTable`  |  <pre>mapper.generateCreateTableRequest(Customer.class)</pre> 前面的语句生成一个低级别的创建表请求；用户必须在 DynamoDB 客户端中调用 `createTable`。  |  <pre>table.createTable(createTableRequest)<br /><br />table.createTable(r -> r.provisionedThroughput(defaultThroughput())<br />    .globalSecondaryIndices(<br />        EnhancedGlobalSecondaryIndex.builder()<br />            .indexName("gsi_1")<br />            .projection(p -> p.projectionType(ProjectionType.ALL))<br />            .provisionedThroughput(defaultThroughput())<br />            .build()));</pre>  | 
|  在 DynamoDB 中执行并行扫描 **DynamoDB 操作：**使用 `Segment` 和 `TotalSegments` 参数执行 `Scan`  |  <pre>mapper.parallelScan(Customer.class, <br />                    scanExpression, <br />                    numTotalSegments)</pre>  |  用户需要处理工作线程并为每个分段调用 `scan`： <pre>table.scan(r -> r.segment(0).totalSegments(5))</pre>  | 
|  将 Amazon S3 与 DynamoDB 集成以存储智能 S3 链接  |  <pre>mapper.createS3Link(bucket, key)<br />mapper.getS3ClientCache()</pre>  |  不支持，因为它将 Amazon S3 与 DynamoDB 结合在一起。  | 

## 映射类和属性
<a name="dynamodb-mapping-schemas"></a>

在 V1 和 V2 中，都使用 bean 样式的注释将类映射到表。V2 还提供了[其他方法来为特定使用案例定义架构](ddb-en-client-adv-features.md#ddb-en-client-adv-features-schm-overview)，例如使用不可变类。

### bean 注释
<a name="dynamodb-mapping-schemas-annos"></a>

下表显示了 V1 和 V2 中使用的特定使用案例的等效 bean 注释。`Customer` 类场景用于说明参数。

V2 中的注释以及类和枚举遵循驼峰大小写惯例，使用 “” 而不是 “DynamoDB”。DynamoDb


| 使用案例 | V1 | V2 | 
| --- | --- | --- | 
| 将类映射到表 |  <pre>@DynamoDBTable (tableName ="CustomerTable")</pre>  | <pre>@DynamoDbBean<br />@DynamoDbBean(converterProviders = {...})</pre>表名在调用 DynamoDbEnhancedClient\$1table() 方法时定义。 | 
| 将类成员指定为表属性  |  <pre>@DynamoDBAttribute(attributeName = "customerName")</pre>  |  <pre>@DynamoDbAttribute("customerName") </pre>  | 
| 指定班级成员是 hash/partition 关键 |  <pre>@DynamoDBHashKey </pre>  |  <pre>@DynamoDbPartitionKey</pre>  | 
| 指定班级成员是 range/sort 关键 |  <pre>@DynamoDBRangeKey </pre>  |  <pre>@DynamoDbSortKey </pre>  | 
| 将类成员指定为二级索引哈希键/分区键 |  <pre>@DynamoDBIndexHashKey </pre>  |  <pre>@DynamoDbSecondaryPartitionKey </pre>  | 
| 将类成员指定为二级索引范围键/排序键 |  <pre>@DynamoDBIndexRangeKey </pre>  |  <pre>@DynamoDbSecondarySortKey </pre>  | 
| 映射到表时忽略此类成员 |  <pre>@DynamoDBIgnore </pre>  |  <pre>@DynamoDbIgnore</pre>  | 
| 将类成员指定为自动生成的 UUID 键属性 |  <pre>@DynamoDBAutoGeneratedKey</pre>  |  <pre>@DynamoDbAutoGeneratedUuid </pre> 默认情况下，不加载提供此功能的扩展；您必须将扩展添加到客户端生成器中。  | 
| 将类成员指定为自动生成的时间戳属性 |  <pre>@DynamoDBAutoGeneratedTimestamp</pre>  |  <pre>@DynamoDbAutoGeneratedTimestampAttribute</pre> 默认情况下，不加载提供此功能的扩展；您必须将扩展添加到客户端生成器中。  | 
| 将类成员指定为自动递增的版本属性 |  <pre>@DynamoDBVersionAttribute</pre>  |  <pre>@DynamoDbVersionAttribute</pre> 提供此功能的扩展会自动加载。  | 
| 将类成员指定为需要自定义转换 |  <pre>@DynamoDBTypeConverted</pre>  |  <pre>@DynamoDbConvertedBy</pre>  | 
| 将类成员指定为存储为另一个属性类型 |  <pre>@DynamoDBTyped(<DynamoDBAttributeType>)</pre>  |  使用 `AttributeConverter` 实现。V2 为常用 Java 类型提供许多内置转换器。您也可以实施自己的自定义 `AttributeConverter` 或 `AttributeConverterProvider`。请参阅本指南中的[控制属性转换](ddb-en-client-adv-features-conversion.md)。  | 
| 指定一个可以序列化为 DynamoDB 文档（JSON 风格的文档）或子文档的类  |  <pre>@DynamoDBDocument</pre>  | 使用增强型文档 API。请参阅以下资源：[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/sdk-for-java/latest/developer-guide/dynamodb-mapping-api-changes.html) | 

### V2 附加注释
<a name="dynamodb-mapping-schemas-annos-v2-addnl"></a>


| 使用案例 | V1 | V2 | 
| --- | --- | --- | 
| 如果 Java 值为 null，则将类成员指定为不存储为 Null 属性 | 不适用 |  <pre>@DynamoDbIgnoreNulls</pre>  | 
| 如果所有属性都为 null，则将类成员指定为空对象 | 不适用 |  <pre>@DynamoDbPreserveEmptyObject</pre>  | 
| 为类成员指定特殊更新操作 | 不适用 |  <pre>@DynamoDbUpdateBehavior</pre>  | 
| 指定一个不可变类 | 不适用 |  <pre>@DynamoDbImmutable</pre>  | 
| 将类成员指定为自动递增的计数器属性 | 不适用 |  <pre>@DynamoDbAtomicCounter</pre> 提供此功能的扩展会自动加载。  | 

## 配置
<a name="dynamodb-mapping-configuration"></a>

在 V1 中，通常使用 `DynamoDBMapperConfig` 的实例来控制特定行为。您可以在创建映射器时或在发出请求时提供配置对象。在 V2 中，配置特定于操作的请求对象。


| 使用案例 | V1 | V1 中的默认设置 | V2 | 
| --- | --- | --- | --- | 
|  |  <pre>DynamoDBMapperConfig.builder()</pre>  |  |  | 
| Batch load/write 重试策略 |  <pre>  .withBatchLoadRetryStrategy(loadRetryStrategy)</pre> <pre>  .withBatchWriteRetryStrategy(writeRetryStrategy)</pre>  | 重试失败的项目 | 在底层 DynamoDBClient 中配置重试策略。请参阅本指南中的[在中配置重试行为 AWS SDK for Java 2.x](retry-strategy.md)。 | 
| 一致性读取 |  <pre>  .withConsistentReads(CONSISTENT)</pre>  | EVENTUAL | 默认情况下，读取操作的一致性读取为 false。在请求对象上使用 .consistentRead(true) 覆盖。 | 
| 包含编组器/解组器集合的转换架构 |  <pre>  .withConversionSchema(conversionSchema)</pre> 静态实现与旧版本向后兼容。  | V2\$1COMPATIBLE | 不适用。这是一项遗留特性，指的是 DynamoDB 的最早版本（V1）存储数据类型的方式，而在增强型客户端中不再保留此行为。DynamoDB V1 中的一个行为示例是将布尔值存储为数字而不是布尔值。 | 
| 表名称 |  <pre>  .withObjectTableNameResolver()<br />  .withTableNameOverride() <br />  .withTableNameResolver()</pre> 静态实现与旧版本向后兼容  | 使用注释或根据类来推断 |  表名在调用 `DynamoDbEnhancedClient#table()` 方法时定义。  | 
| 分页加载策略 |  <pre>  .withPaginationLoadingStrategy(strategy)</pre>  选项包括：LAZY\$1`LOADING`、`EAGER_LOADING` 或 `ITERATION_ONLY`  | LAZY\$1LOADING |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/sdk-for-java/latest/developer-guide/dynamodb-mapping-api-changes.html)  | 
| 请求指标收集 |  <pre>  .withRequestMetricCollector(collector)</pre>  | null | 在构建标准 DynamoDB 客户端时在 ClientOverrideConfiguration 中使用 metricPublisher()。 | 
| 保存行为 |  <pre>  .withSaveBehavior(SaveBehavior.CLOBBER) </pre> 选项包括 `UPDATE`、`CLOBBER`、`PUT`、`APPEND_SET` 或 `UPDATE_SKIP_NULL_ATTRIBUTES`。  | UPDATE |  在 V2 中，您可以显式调用 `putItem()` 或 `updateItem()`。 `CLOBBER or PUT`：V2 中的相应动作是调用 `putItem()`。没有特定的 `CLOBBER` 配置。 `UPDATE`: 对应于 `updateItem()` `UPDATE_SKIP_NULL_ATTRIBUTES`: 对应于`updateItem()`。使用请求设置 `ignoreNulls` 和注释/标签 `DynamoDbUpdateBehavior` 控制更新行为。 `APPEND_SET`：不支持  | 
| 类型转换器工厂 |  <pre>  .withTypeConverterFactory(typeConverterFactory) </pre>  | 标准类型转换器 |  使用以下方式在 bean 上设置 <pre>@DynamoDbBean(converterProviders = {ConverterProvider.class, <br />        DefaultAttributeConverterProvider.class})</pre>  | 

### 每个操作的配置
<a name="dynamodb-mapping-configuration-per-op"></a>

在 V1 中，某些操作（例如 `query()`）可以通过提交给操作的“表达式”对象来进行高度灵活的配置。例如：

```
DynamoDBQueryExpression<Customer> emailBwQueryExpr = new DynamoDBQueryExpression<Customer>()
    .withRangeKeyCondition("Email",
        new Condition()
            .withComparisonOperator(ComparisonOperator.BEGINS_WITH)
            .withAttributeValueList(
                new AttributeValue().withS("my")));

mapper.query(Customer.class, emailBwQueryExpr);
```

在 V2 中，不使用配置对象，而是使用生成器对请求对象设置参数。例如：

```
QueryEnhancedRequest emailBw = QueryEnhancedRequest.builder()
    .queryConditional(QueryConditional
        .sortBeginsWith(kb -> kb
            .sortValue("my"))).build();

customerTable.query(emailBw);
```

## 条件
<a name="dynamodb-mapping-conditionals"></a>

在 V2 中，条件表达式和筛选表达式使用 `Expression` 对象来表达，该对象封装了条件以及名称和筛选条件的映射。


| 使用案例 | 操作 | V1 | V2 | 
| --- | --- | --- | --- | 
| 预期的属性条件 | save()、delete()、query()、scan() |  <pre>new DynamoDBSaveExpression()<br />  .withExpected(Collections.singletonMap(<br />      "otherAttribute", new ExpectedAttributeValue(false)))<br />  .withConditionalOperator(ConditionalOperator.AND);</pre>  | 已弃用；改用 ConditionExpression。 | 
| 条件表达式 | delete() |  <pre>deleteExpression.setConditionExpression("zipcode = :zipcode")<br />deleteExpression.setExpressionAttributeValues(...)<br /></pre>  |  <pre>Expression conditionExpression =<br />    Expression.builder()<br />        .expression("#key = :value OR #key1 = :value1")<br />        .putExpressionName("#key", "attribute")<br />        .putExpressionName("#key1", "attribute3")<br />        .putExpressionValue(":value", AttributeValues.stringValue("wrong"))<br />        .putExpressionValue(":value1", AttributeValues.stringValue("three"))<br />        .build();<br /><br />DeleteItemEnhancedRequest request = DeleteItemEnhancedRequest.builder()<br />         .conditionExpression(conditionExpression).build();</pre>  | 
| 筛选表达式 | query()、scan() |  <pre>scanExpression<br />  .withFilterExpression("#statename = :state")<br />  .withExpressionAttributeValues(attributeValueMapBuilder.build())<br />  .withExpressionAttributeNames(attributeNameMapBuilder.build())<br /></pre>  |  <pre>Map<String, AttributeValue> values = singletonMap(":key", stringValue("value"));<br />Expression filterExpression =<br />    Expression.builder()<br />        .expression("name = :key")<br />        .expressionValues(values)<br />        .build();<br />QueryEnhancedRequest request = QueryEnhancedRequest.builder()<br />    .filterExpression(filterExpression).build();<br /></pre>  | 
| 查询的条件表达式 | query() |  <pre>queryExpression.withKeyConditionExpression()</pre>  |  <pre>QueryConditional keyEqual = QueryConditional.keyEqualTo(b -> b<br />                .partitionValue("movie01"));<br /><br />QueryEnhancedRequest tableQuery = QueryEnhancedRequest.builder()<br />                .queryConditional(keyEqual)<br />                .build();</pre>  | 

## 类型转换
<a name="dynamodb-mapping-type-conv"></a>

### 默认转换器
<a name="dynamodb-mapping-type-conv-defaults"></a>

在 V2 中，SDK 为所有常见类型提供了一组默认转换器。既可以在整体提供程序级别更改类型转换器，也可以为单个属性更改类型转换器。您可以在 [AttributeConverter](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/AttributeConverter.html)API 参考中找到可用转换器的列表。

### 为属性设置自定义转换器
<a name="dynamodb-mapping-type-conv-anno"></a>

在 V1 中，您可以使用 `@DynamoDBTypeConverted` 对 getter 方法添加注释，以指定在 Java 属性类型和 DynamoDB 属性类型之间进行转换的类。例如，`CurrencyFormatConverter` 可以在 Java `Currency` 类型和 DynamoDB 字符串之间应用转换，如以下代码段所示。

```
@DynamoDBTypeConverted(converter = CurrencyFormatConverter.class)
public Currency getCurrency() { return currency; }
```

上一个代码段的 V2 等效代码如下所示。

```
@DynamoDbConvertedBy(CurrencyFormatConverter.class)
public Currency getCurrency() { return currency; }
```

**注意**  
在 V1 中，您可以将注释应用于属性本身、类型或用户定义的注释，V2 仅支持将注释应用于 getter。

### 添加类型转换器工厂或提供程序
<a name="dynamodb-mapping-type-conv-factory"></a>

在 V1 中，您可以提供自己的类型转换器集，也可以通过在配置中添加类型转换器工厂来覆盖您在意的类型。类型转换器工厂扩展 `DynamoDBTypeConverterFactory`，通过获取对默认集的引用并对其进行扩展来完成覆盖。以下代码段演示了如何执行此操作。

```
DynamoDBTypeConverterFactory typeConverterFactory =
    DynamoDBTypeConverterFactory.standard().override()
        .with(String.class, CustomBoolean.class, new DynamoDBTypeConverter<String, CustomBoolean>() {
            @Override
            public String convert(CustomBoolean bool) {
                return String.valueOf(bool.getValue());
            }
            @Override
            public CustomBoolean unconvert(String string) {
                return new CustomBoolean(Boolean.valueOf(string));
            }}).build();
DynamoDBMapperConfig config =
    DynamoDBMapperConfig.builder()
        .withTypeConverterFactory(typeConverterFactory)
        .build();
DynamoDBMapper mapperWithTypeConverterFactory = new DynamoDBMapper(dynamo, config);
```

V2 通过 `@DynamoDbBean` 注释提供了类似的功能。您可以提供单个 `AttributeConverterProvider` 或一个有序的 `AttributeConverterProvider` 链。请注意，如果您提供自己的属性转换器提供程序链，则将覆盖默认的转换器提供程序，且必须在链中包括它才能使用其属性转换器。

```
@DynamoDbBean(converterProviders = {
   ConverterProvider1.class, 
   ConverterProvider2.class,
   DefaultAttributeConverterProvider.class})
public class Customer {
  ...
}
```

本指南中关于[属性转换](ddb-en-client-adv-features-conversion.md#ddb-en-client-adv-features-conversion-example)的部分包含了 V2 的完整示例。

# 适用于 Java 的 SDK 版本 1 和版本 2 之间的字符串处理差异
<a name="dynamodb-migration-string-handling"></a>

向 DynamoDB 发送数据时，V1 和 V2 对空字符串的处理方式有所不同：
+ **V1**：在发送到 DynamoDB 之前将空字符串转换为 null 值（导致没有属性）
+ **V2**：将空字符串作为实际的空字符串值发送给 DynamoDB

**重要**  
迁移到 V2 后，如果您不想在 DynamoDB 中存储空字符串，则必须实施自定义转换器。如果没有自定义转换器，V2 会将空字符串作为实际的空字符串属性存储在 DynamoDB 项目中，这与 V1 完全省略这些属性的行为不同。

**Example V2 的自定义转换器，用于将空字符串属性转换为 null**  

```
/**
 * Custom converter that maintains V1 behavior by converting empty strings to null values
 * when writing to DynamoDB, ensuring compatibility with existing data. No attribute will be saved to DynamoDB.
 */
public class NullifyEmptyStringConverter implements AttributeConverter<String> {
    @Override
    public AttributeValue transformFrom(String value) {
        if (value == null || value.isEmpty()) {
            return AttributeValue.builder().nul(true).build();
        }
        return AttributeValue.builder().s(value).build();
    }

    @Override
    public String transformTo(AttributeValue attributeValue) {
        if (attributeValue.nul()) {
            return null;
        }
        return attributeValue.s();
    }

    @Override
    public EnhancedType<String> type() {
        return EnhancedType.of(String.class);
    }

    @Override
    public AttributeValueType attributeValueType() {
        return AttributeValueType.S;
    }
}

// V2 usage:
@DynamoDbBean
public class Customer {
    private String name;

    @DynamoDbConvertedBy(NullifyEmptyStringConverter.class)
    public String getName() {
        return name;
    }
}
```



# 适用于 Java 的 SDK 版本 1 和版本 2 之间的乐观锁差异
<a name="dynamodb-migrate-optimstic-locking"></a>

V1 和 V2 都使用属性注释来实施乐观锁，注释在 bean 类上标记一个属性以存储版本号。


**乐观锁行为的差异**  

|  | V1 | V2 | 
| --- | --- | --- | 
| bean 类注释 | @DynamoDBVersionAttribute | @DynamoDbVersionAttribute（请注意，V2 使用小写的“b”） | 
| 初始保存 | 版本号属性设置为 1。 |  使用 `@DynamoDbVersionAttribute(startAt = X)` 设置的版本属性的起始值。默认值为 0。  | 
| 更新 | 如果条件检查确认正在更新的对象的版本号与数据库中的版本号相匹配，则版本号属性将增加 1。 |  如果条件检查确认正在更新的对象的版本号与数据库中的版本号相匹配，则版本号属性会递增。 按照使用 `@DynamoDbVersionAttribute(incrementBy = X)` 设置的 `incrementBy` 选项来增加版本号属性。默认值为 1。  | 
| 删除 | DynamoDBMapper 添加条件检查，以确认要删除的对象的版本号与数据库中的版本号相匹配。 |  V2 不会自动为删除操作添加条件。如果要控制删除行为，则必须手动添加条件表达式。 在以下示例中，`recordVersion` 是 bean 的版本属性。 <pre>// 1. Read the item and get its current version.<br />Customer item = customerTable.getItem(Key.builder().partitionValue("someId").build());<br />AttributeValue currentVersion = item.getRecordVersion();<br /><br />// 2. Create conditional delete with the `currentVersion` value.<br />DeleteItemEnhancedRequest deleteItemRequest =<br />    DeleteItemEnhancedRequest.builder()<br />       .key(KEY)<br />       .conditionExpression(Expression.builder()<br />           .expression("recordVersion = :current_version_value")<br />           .putExpressionValue(":current_version_value", currentVersion)<br />           .build()).build();<br /><br />customerTable.deleteItem(deleteItemRequest);</pre>  | 
| 带条件检查的事务写入 | 您不能使用在 addConditionCheck 方法中用 @DynamoDBVersionAttribute 进行注释的 bean 类。 | 您可以在 addConditionCheck 生成器方法中通过 @DynamoDbVersionAttribute 注释为 transactWriteItems 请求使用 bean 类。 | 
| 禁用 | 通过将  DynamoDBMapperConfig.SaveBehavior 枚举值从 UPDATE 更改为 CLOBBER 来禁用乐观锁。 |  不使用 `@DynamoDbVersionAttribute` 注释。  | 

# 适用于 Java 的 SDK 版本 1 和版本 2 之间的 fluent setter 方法差异
<a name="dynamodb-migrate-fluent-setters"></a>

你可以在 V1 的 DynamoDB 映射 API 中使用 POJOs 流畅的设置器，也可以在 2.30.29 版本之后的 V2 中使用。

例如，以下 POJO 通过 `setName` 方法返回一个 `Customer` 实例：

```
// V1

@DynamoDBTable(tableName ="Customer")
public class Customer{
  private String name;
  // Other attributes and methods not shown.
  public Customer setName(String name){
     this.name = name;
     return this;
  }
}
```

但是，如果您使用的是 2.30.29 之前的 V2 版本，则 `setName` 会返回 `Customer` 实例，其 `name` 值为 `null`。

```
// V2 prior to version 2.30.29.

@DynamoDbBean
public class Customer{
  private String name;
  // Other attributes and methods not shown.
  public Customer setName(String name){ 
     this.name = name;
     return this;  // Bug: returns this instance with a `name` value of `null`.
  }
}
```

```
// Available in V2 since version 2.30.29.

@DynamoDbBean
public class Customer{
  private String name;
  // Other attributes and methods not shown.
  public Customer setName(String name){ 
     this.name = name;
     return this;  // Returns this instance for method chaining with the `name` value set.
  }
}
```