

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 了解 DynamoDB 增強型用戶端 API 的基本概念
<a name="ddb-en-client-use"></a>

本主題討論 DynamoDB 增強型用戶端 API 的基本功能，並將其與[標準 DynamoDB 用戶端 API](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/package-summary.html) 進行比較。

如果您是初次使用 DynamoDB 增強型用戶端 API，我們建議您瀏覽[簡介教學](ddb-en-client-getting-started.md)課程，熟悉基礎課程。

## Java 中的 DynamoDB 項目
<a name="ddb-en-client-use-usecase"></a>

DynamoDB 資料表存放項目。根據您的使用案例，Java 端的項目可以採用靜態結構化資料或動態建立的結構形式。

如果您的使用案例呼叫具有一組一致屬性的項目，請使用[註釋類別](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-anno-bean)或使用[建置器](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-builder)來產生適當的靜態類型 `TableSchema`。

或者，如果您需要存放由不同結構組成的項目，請建立 `DocumentTableSchema`。 `DocumentTableSchema`是[增強型文件 API](ddb-en-client-doc-api.md) 的一部分，只需要靜態類型的主索引鍵，並使用`EnhancedDocument`執行個體來保存資料元素。另一個[主題涵蓋了增強型文件 API。](ddb-en-client-doc-api.md)

## 資料模型類別的屬性類型
<a name="ddb-en-client-use-types"></a>

雖然 DynamoDB 相較於 Java 的豐富類型系統支援[少量屬性類型](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html#HowItWorks.DataTypes)，但 DynamoDB 增強型用戶端 API 提供將 Java 類別成員轉換為 DynamoDB 屬性類型的機制。

Java 資料類別的屬性類型 （屬性） 應為物件類型，而非基本類型。例如， 一律使用 `Long`和 `Integer` 物件資料類型，而不是 `int` `long`和 基本資料類型。

根據預設，DynamoDB 增強型用戶端 API 支援多種類型的屬性轉換器，例如 [Integer](https://docs.oracle.com/javase/8/docs/api/java/lang/Integer.html)、[String](https://docs.oracle.com/javase/8/docs/api/java/lang/String.html)、[BigDecimal](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/internal/converter/attribute/BigDecimalAttributeConverter.html) 和 [Instant](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/internal/converter/attribute/InstantAsStringAttributeConverter.html)。清單會出現在 [AttributeConverter 界面的已知實作類別](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/AttributeConverter.html)中。此清單包含許多類型和集合，例如地圖、清單和集合。

若要存放預設不支援或不符合 JavaBean 慣例之屬性類型的資料，您可以撰寫自訂`AttributeConverter`實作來執行轉換。如需[範例](ddb-en-client-adv-features-conversion.md#ddb-en-client-adv-features-conversion-example)，請參閱屬性轉換一節。

若要存放屬性類型的資料，其類別符合 Java Bean 規格 （或[不可變的資料類別](ddb-en-client-use-immut.md))，您可以採取兩種方法。
+ 如果您可以存取來源檔案，則可以使用 `@DynamoDbBean`（或 ) 註釋 類別`@DynamoDbImmutable`。討論巢狀屬性的 區段顯示使用註釋類別[的範例](ddb-en-client-adv-features-nested.md#ddb-en-client-adv-features-nested-map-anno)。
+ 如果 無法存取 屬性的 JavaBean 資料類別來源檔案 （或者您不想註釋您有權存取之類別的來源檔案），則可以使用建置器方法。這會在不定義索引鍵的情況下建立資料表結構描述。然後，您可以在另一個資料表結構描述內將此資料表結構描述巢狀化，以執行映射。巢狀屬性區段[的範例](ddb-en-client-adv-features-nested.md#ddb-en-client-adv-features-nested-map-builder)顯示巢狀結構描述的使用。

### Null 值
<a name="ddb-en-client-use-types-nulls"></a>

當您使用 `putItem`方法時，增強型用戶端不會在對 DynamoDB 的請求中包含映射資料物件的 null 值屬性。

SDK 的`updateItem`請求預設行為會從 DynamoDB 中的項目中移除屬性，該項目在方法中提交的 物件中設定為 null`updateItem`。如果您想要更新一些屬性值並保持其他屬性值不變，您有兩個選項。
+ 在您變更值之前擷取項目 （使用 `getItem`)。透過使用此方法，開發套件會將所有更新值和舊值提交至 DynamoDB。
+ 當您建置更新項目的請求`IgnoreNullsMode.MAPS_ONLY`時，請使用 `[IgnoreNullsMode](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/IgnoreNullsMode.html).SCALAR_ONLY`或 。兩種模式都會忽略代表 DynamoDB 中純量屬性之物件中的 null 值屬性。本指南中的[更新包含複雜類型的項目](ddb-en-client-adv-features-nested.md#ddb-en-client-adv-features-nested-updates)主題包含有關`IgnoreNullsMode`值以及如何使用複雜類型的詳細資訊。

下列範例示範 `updateItem()`方法`ignoreNullsMode()`的 。

```
    public static void updateItemNullsExample() {
        Customer customer = new Customer();
        customer.setCustName("CustomerName");
        customer.setEmail("email");
        customer.setId("1");
        customer.setRegistrationDate(Instant.now());

        logger.info("Original customer: {}", customer);

        // Put item with values for all attributes.
        try {
            customerAsyncDynamoDbTable.putItem(customer).join();
        } catch (RuntimeException rte) {
            logger.error("A exception occurred during putItem: {}", rte.getCause().getMessage(), rte);
            return;
        }

        // Create a Customer instance with the same 'id' and 'email' values, but a different 'name' value.
        // Do not set the 'registrationDate' attribute.
        Customer customerForUpdate = new Customer();
        customerForUpdate.setCustName("NewName");
        customerForUpdate.setEmail("email");
        customerForUpdate.setId("1");

        // Update item without setting the 'registrationDate' property and set IgnoreNullsMode to SCALAR_ONLY.
        try {
            Customer updatedWithNullsIgnored = customerAsyncDynamoDbTable.updateItem(b -> b
                            .item(customerForUpdate)
                            .ignoreNullsMode(IgnoreNullsMode.SCALAR_ONLY))
                    .join();
            logger.info("Customer updated with nulls ignored: {}", updatedWithNullsIgnored.toString());
        } catch (RuntimeException rte) {
            logger.error("An exception occurred during updateItem: {}", rte.getCause().getMessage(), rte);
            return;
        }

        // Update item without setting the registrationDate attribute and not setting ignoreNulls to true.
        try {
            Customer updatedWithNullsUsed = customerAsyncDynamoDbTable.updateItem(customerForUpdate)
                    .join();
            logger.info("Customer updated with nulls used: {}", updatedWithNullsUsed.toString());
        } catch (RuntimeException rte) {
            logger.error("An exception occurred during updateItem: {}", rte.getCause().getMessage(), rte);
        }
    }


// Logged lines. 
Original customer: Customer [id=1, name=CustomerName, email=email, regDate=2024-10-11T14:12:30.222858Z]
Customer updated with nulls ignored: Customer [id=1, name=NewName, email=email, regDate=2024-10-11T14:12:30.222858Z]
Customer updated with nulls used: Customer [id=1, name=NewName, email=email, regDate=null]
```

## DynamoDB 增強型用戶端基本方法
<a name="ddb-en-client-use-basic-ops"></a>

增強型用戶端的基本方法會映射到其命名所用的 DynamoDB 服務操作。下列範例顯示每個方法的最簡單變化。您可以透過傳入增強型請求物件來自訂每個方法。增強型請求物件提供標準 DynamoDB 用戶端中大多數可用的功能。它們完全記錄在 AWS SDK for Java 2.x API 參考中。

此範例使用先前[`Customer` 類別](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-anno-bean-cust)顯示的 。

```
// CreateTable
customerTable.createTable();

// GetItem
Customer customer = customerTable.getItem(Key.builder().partitionValue("a123").build());

// UpdateItem
Customer updatedCustomer = customerTable.updateItem(customer);

// PutItem
customerTable.putItem(customer);

// DeleteItem
Customer deletedCustomer = customerTable.deleteItem(Key.builder().partitionValue("a123").sortValue(456).build());

// Query
PageIterable<Customer> customers = customerTable.query(keyEqualTo(k -> k.partitionValue("a123")));

// Scan
PageIterable<Customer> customers = customerTable.scan();

// BatchGetItem
BatchGetResultPageIterable batchResults = 
    enhancedClient.batchGetItem(r -> r.addReadBatch(ReadBatch.builder(Customer.class)
                                      .mappedTableResource(customerTable)
                                      .addGetItem(key1)
                                      .addGetItem(key2)
                                      .addGetItem(key3)
                                      .build()));

// BatchWriteItem
batchResults = enhancedClient.batchWriteItem(r -> r.addWriteBatch(WriteBatch.builder(Customer.class)
                                                   .mappedTableResource(customerTable)
                                                   .addPutItem(customer)
                                                   .addDeleteItem(key1)
                                                   .addDeleteItem(key1)
                                                   .build()));

// TransactGetItems
transactResults = enhancedClient.transactGetItems(r -> r.addGetItem(customerTable, key1)
                                                        .addGetItem(customerTable, key2));

// TransactWriteItems
enhancedClient.transactWriteItems(r -> r.addConditionCheck(customerTable, 
                                                           i -> i.key(orderKey)
                                                                 .conditionExpression(conditionExpression))
                                        .addUpdateItem(customerTable, customer)
                                        .addDeleteItem(customerTable, key));
```

## 比較 DynamoDB 增強型用戶端與標準 DynamoDB 用戶端
<a name="ddb-en-client-use-compare"></a>

DynamoDB 用戶端 APIs — [標準](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/package-summary.html)和[增強](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/package-summary.html)型 — 可讓您使用 DynamoDB 資料表來執行 CRUD （建立、讀取、更新和刪除） 資料層級操作。用戶端 APIs 之間的差異在於其完成方式。使用標準用戶端，您可以直接使用低階資料屬性。增強型用戶端 API 使用熟悉的 Java 類別，並映射到幕後的低階 API。

雖然兩個用戶端 APIs 都支援資料層級操作，但標準 DynamoDB 用戶端也支援資源層級操作。資源層級操作會管理資料庫，例如建立備份、列出資料表和更新資料表。增強型用戶端 API 支援選取數量的資源層級操作，例如建立、描述和刪除資料表。

為了說明兩個用戶端 APIs 使用的不同方法，下列程式碼範例顯示使用標準用戶端和增強型用戶端建立相同的`ProductCatalog`資料表。

### 比較：使用標準 DynamoDB 用戶端建立資料表
<a name="ddb-en-client-use-compare-cs1"></a>

```
DependencyFactory.dynamoDbClient().createTable(builder -> builder
        .tableName(TABLE_NAME)
        .attributeDefinitions(
                b -> b.attributeName("id").attributeType(ScalarAttributeType.N),
                b -> b.attributeName("title").attributeType(ScalarAttributeType.S),
                b -> b.attributeName("isbn").attributeType(ScalarAttributeType.S)
        )
        .keySchema(
                builder1 -> builder1.attributeName("id").keyType(KeyType.HASH),
                builder2 -> builder2.attributeName("title").keyType(KeyType.RANGE)
        )
        .globalSecondaryIndexes(builder3 -> builder3
                        .indexName("products_by_isbn")
                        .keySchema(builder2 -> builder2
                                .attributeName("isbn").keyType(KeyType.HASH))
                        .projection(builder2 -> builder2
                                .projectionType(ProjectionType.INCLUDE)
                                .nonKeyAttributes("price", "authors"))
                        .provisionedThroughput(builder4 -> builder4
                                .writeCapacityUnits(5L).readCapacityUnits(5L))
        )
        .provisionedThroughput(builder1 -> builder1
                .readCapacityUnits(5L).writeCapacityUnits(5L))
);
```

### 比較：使用 DynamoDB 增強型用戶端建立資料表
<a name="ddb-en-client-use-compare-cs2"></a>

```
DynamoDbEnhancedClient enhancedClient = DependencyFactory.enhancedClient();
productCatalog = enhancedClient.table(TABLE_NAME, TableSchema.fromImmutableClass(ProductCatalog.class));
productCatalog.createTable(b -> b
        .provisionedThroughput(b1 -> b1.readCapacityUnits(5L).writeCapacityUnits(5L))
        .globalSecondaryIndices(b2 -> b2.indexName("products_by_isbn")
                .projection(b4 -> b4
                        .projectionType(ProjectionType.INCLUDE)
                        .nonKeyAttributes("price", "authors"))
                .provisionedThroughput(b3 -> b3.writeCapacityUnits(5L).readCapacityUnits(5L))
        )
);
```

增強型用戶端使用下列標註的資料類別。DynamoDB 增強型用戶端會將 Java 資料類型映射至 DynamoDB 資料類型，以取得較不詳細的程式碼，更容易遵循。 `ProductCatalog` 是搭配 DynamoDB 增強型用戶端使用不可變類別的範例。本主題[稍後將討論](ddb-en-client-use-immut.md)對映射資料類別使用不可變類別。

### `ProductCatalog` 類別
<a name="ddb-en-client-use-compare-cs3"></a>

```
package org.example.tests.model;

import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbIgnore;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbImmutable;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSecondaryPartitionKey;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSortKey;

import java.math.BigDecimal;
import java.util.Objects;
import java.util.Set;

@DynamoDbImmutable(builder = ProductCatalog.Builder.class)
public class ProductCatalog implements Comparable<ProductCatalog> {
    private Integer id;
    private String title;
    private String isbn;
    private Set<String> authors;
    private BigDecimal price;


    private ProductCatalog(Builder builder){
        this.authors = builder.authors;
        this.id = builder.id;
        this.isbn = builder.isbn;
        this.price = builder.price;
        this.title = builder.title;
    }

    public static Builder builder(){ return new Builder(); }

    @DynamoDbPartitionKey
    public Integer id() { return id; }
    
    @DynamoDbSortKey
    public String title() { return title; }
    
    @DynamoDbSecondaryPartitionKey(indexNames = "products_by_isbn")
    public String isbn() { return isbn; }
    public Set<String> authors() { return authors; }
    public BigDecimal price() { return price; }


    public static final class Builder {
      private Integer id;
      private String title;
      private String isbn;
      private Set<String> authors;
      private BigDecimal price;
      private Builder(){}

      public Builder id(Integer id) { this.id = id; return this; }
      public Builder title(String title) { this.title = title; return this; }
      public Builder isbn(String ISBN) { this.isbn = ISBN; return this; }
      public Builder authors(Set<String> authors) { this.authors = authors; return this; }
      public Builder price(BigDecimal price) { this.price = price; return this; }
      public ProductCatalog build() { return new ProductCatalog(this); }
  }

    @Override
    public String toString() {
        final StringBuffer sb = new StringBuffer("ProductCatalog{");
        sb.append("id=").append(id);
        sb.append(", title='").append(title).append('\'');
        sb.append(", isbn='").append(isbn).append('\'');
        sb.append(", authors=").append(authors);
        sb.append(", price=").append(price);
        sb.append('}');
        return sb.toString();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        ProductCatalog that = (ProductCatalog) o;
        return id.equals(that.id) && title.equals(that.title) && Objects.equals(isbn, that.isbn) && Objects.equals(authors, that.authors) && Objects.equals(price, that.price);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, title, isbn, authors, price);
    }

    @Override
    @DynamoDbIgnore
    public int compareTo(ProductCatalog other) {
        if (this.id.compareTo(other.id) != 0){
            return this.id.compareTo(other.id);
        } else {
            return this.title.compareTo(other.title);
        }
    }
}
```

以下兩個批次寫入的程式碼範例說明使用標準用戶端而非增強型用戶端時的詳細程度和缺乏類型安全性。

### 比較：使用標準 DynamoDB 用戶端進行批次寫入
<a name="ddb-en-client-use-compare-cs4"></a>

```
    public static void batchWriteStandard(DynamoDbClient dynamoDbClient, String tableName) {

        Map<String, AttributeValue> catalogItem = Map.of(
                "authors", AttributeValue.builder().ss("a", "b").build(),
                "id", AttributeValue.builder().n("1").build(),
                "isbn", AttributeValue.builder().s("1-565-85698").build(),
                "title", AttributeValue.builder().s("Title 1").build(),
                "price", AttributeValue.builder().n("52.13").build());

        Map<String, AttributeValue> catalogItem2 = Map.of(
                "authors", AttributeValue.builder().ss("a", "b", "c").build(),
                "id", AttributeValue.builder().n("2").build(),
                "isbn", AttributeValue.builder().s("1-208-98073").build(),
                "title", AttributeValue.builder().s("Title 2").build(),
                "price", AttributeValue.builder().n("21.99").build());

        Map<String, AttributeValue> catalogItem3 = Map.of(
                "authors", AttributeValue.builder().ss("g", "k", "c").build(),
                "id", AttributeValue.builder().n("3").build(),
                "isbn", AttributeValue.builder().s("7-236-98618").build(),
                "title", AttributeValue.builder().s("Title 3").build(),
                "price", AttributeValue.builder().n("42.00").build());

        Set<WriteRequest> writeRequests = Set.of(
                WriteRequest.builder().putRequest(b -> b.item(catalogItem)).build(),
                WriteRequest.builder().putRequest(b -> b.item(catalogItem2)).build(),
                WriteRequest.builder().putRequest(b -> b.item(catalogItem3)).build());

        Map<String, Set<WriteRequest>> productCatalogItems = Map.of(
                "ProductCatalog", writeRequests);

        BatchWriteItemResponse response = dynamoDbClient.batchWriteItem(b -> b.requestItems(productCatalogItems));

        logger.info("Unprocessed items: " + response.unprocessedItems().size());
    }
```

### 比較：使用 DynamoDB 增強型用戶端進行批次寫入
<a name="ddb-en-client-use-compare-cs5"></a>

```
    public static void batchWriteEnhanced(DynamoDbTable<ProductCatalog> productCatalog) {
        ProductCatalog prod = ProductCatalog.builder()
                .id(1)
                .isbn("1-565-85698")
                .authors(new HashSet<>(Arrays.asList("a", "b")))
                .price(BigDecimal.valueOf(52.13))
                .title("Title 1")
                .build();
        ProductCatalog prod2 = ProductCatalog.builder()
                .id(2)
                .isbn("1-208-98073")
                .authors(new HashSet<>(Arrays.asList("a", "b", "c")))
                .price(BigDecimal.valueOf(21.99))
                .title("Title 2")
                .build();
        ProductCatalog prod3 = ProductCatalog.builder()
                .id(3)
                .isbn("7-236-98618")
                .authors(new HashSet<>(Arrays.asList("g", "k", "c")))
                .price(BigDecimal.valueOf(42.00))
                .title("Title 3")
                .build();

        BatchWriteResult batchWriteResult = DependencyFactory.enhancedClient()
                .batchWriteItem(b -> b.writeBatches(
                        WriteBatch.builder(ProductCatalog.class)
                                .mappedTableResource(productCatalog)
                                .addPutItem(prod).addPutItem(prod2).addPutItem(prod3)
                                .build()
                ));
        logger.info("Unprocessed items: " + batchWriteResult.unprocessedPutItemsForTable(productCatalog).size());
    }
```