

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

# 使用 程式設計 DynamoDB AWS SDK for Java 2.x
<a name="ProgrammingWithJava"></a>

此程式設計指南為想要搭配 Java 使用 Amazon DynamoDB 的程式設計人員提供指導。本指南涵蓋不同的概念，包括抽象層、組態管理、錯誤處理、控制重試政策和管理保持連線。

**Topics**
+ [關於 AWS SDK for Java 2.x](#AboutProgrammingWithJavaSDK)
+ [開始使用](#GetStartedProgrammingWithJavaSDK)
+ [適用於 Java 2.x 的 SDK 文件](#ProgrammingWithJavaUseDoc)
+ [支援的介面](#JavaInterfaces)
+ [其他程式碼範例](#AdditionalCodeEx)
+ [同步和非同步程式設計](#SyncAsyncProgramming)
+ [HTTP 用戶端](#HttpClients)
+ [Config](#ConfigHttpClient)
+ [錯誤處理](#JavaErrorHandling)
+ [AWS 請求 ID](#JavaRequestID)
+ [日誌](#JavaLogging)
+ [分頁](#JavaPagination)
+ [資料類別註釋](#JavaDataClassAnnotation)

## 關於 AWS SDK for Java 2.x
<a name="AboutProgrammingWithJavaSDK"></a>

您可以使用 官方從 Java 存取 DynamoDB 適用於 Java 的 AWS SDK。適用於 Java 的 SDK 有兩個版本：1.x 和 2.x。1.x 版本已於 2024 年 1 月 12 日[宣布](https://aws.amazon.com/blogs/developer/announcing-end-of-support-for-aws-sdk-for-java-v1-x-on-december-31-2025/)終止支援。它將在 2024 年 7 月 31 日進入維護模式，並將於 2025 年 12 月 31 日終止支援。對於新開發，我們強烈建議您使用 2.x，該版本於 2018 年首次發行。本指南專門針對 2.x，且僅著重於與 DynamoDB 相關的 SDK 部分。

如需 AWS SDKs，請參閱《 [AWS SDK 和工具參考指南》中的 SDK 和工具維護政策和](https://docs.aws.amazon.com/sdkref/latest/guide/maint-policy.html) [AWS SDKs 和工具版本支援矩陣](https://docs.aws.amazon.com/sdkref/latest/guide/version-support-matrix.html)。 *AWS SDKs *

 AWS SDK for Java 2.x 是 1.x 程式碼基底的主要重寫。適用於 Java 2.x 的 SDK 支援現代 Java 功能，例如 Java 8 中引入的非封鎖 I/O。適用於 Java 2.x 的 SDK 也新增了對可插入 HTTP 用戶端實作的支援，以提供更多的網路連線彈性和組態選項。

適用於 Java 1.x 的 SDK 和適用於 Java 2.x 的 SDK 之間的明顯變更是使用新的套件名稱。Java 1.x SDK 使用 `com.amazonaws` 套件名稱，而 Java 2.x SDK 使用 `software.amazon.awssdk`。同樣地，Java 1.x SDK 的 Maven 成品使用 `com.amazonaws` `groupId`，而 Java 2.x SDK 成品則使用 `software.amazon.awssdk` `groupId`。

**重要**  
 適用於 Java 的 AWS SDK 1.x 有一個名為 的 DynamoDB 套件`com.amazonaws.dynamodbv2`。套件名稱中的「v2」不表示其適用於 Java 2 (J2SE)。相反地，「v2」表示套件支援 DynamoDB 低階 API 的[第二個版本](CurrentAPI.md)，而非低階 API 的[原始版本](Appendix.APIv20111205.md)。

### 支援 Java 版本
<a name="SupportedJavaVersions"></a>

 AWS SDK for Java 2.x 提供長期支援 (LTS) [Java 版本](https://github.com/aws/aws-sdk-java-v2?tab=readme-ov-file#maintenance-and-support-for-java-versions)的完整支援。

## 開始使用 AWS SDK for Java 2.x
<a name="GetStartedProgrammingWithJavaSDK"></a>

下列教學課程說明如何使用 [Apache Maven](https://maven.apache.org/) 來定義適用於 Java 2.x 的 SDK 的相依性。本教學課程也說明如何撰寫連線至 DynamoDB 的程式碼，以列出可用的 DynamoDB 資料表。本指南中的教學課程是以《*AWS SDK for Java 2.x 開發人員指南*》中的 [AWS SDK for Java 2.x入門](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html)教學課程為基礎。我們已編輯本教學課程，以呼叫 DynamoDB 而非 Amazon S3。

**Topics**
+ [步驟 1：設定本教學課程](#GetStartedJavaSetup)
+ [步驟 2：建立專案](#GetStartedJavaProjectSetup)
+ [步驟 3：撰寫程式碼](#GetStartedJavaCode)
+ [步驟 4：建置並執行應用程式](#GetStartedRunJava)

### 步驟 1：設定本教學課程
<a name="GetStartedJavaSetup"></a>

在開始本教學課程之前，您需要以下項目：
+ 存取 DynamoDB 的許可。
+ 使用 設定單一登入存取的 Java AWS 服務 開發環境 AWS 存取入口網站。

若要設定本教學課程，請遵循 *AWS SDK for Java 2.x 開發人員指南*中[設定概觀](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/setup.html#setup-overview)中的指示。[設定具有單一登入存取設定的開發環境](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/setup.html#setup-credentials) (針對 Java SDK)，並擁有[作用中的 AWS 存取入口網站工作階段](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/setup.html#setup-login-sso)後，請繼續本教學課程[的步驟 2](#GetStartedJavaProjectSetup)。

### 步驟 2：建立專案
<a name="GetStartedJavaProjectSetup"></a>

若要建立本教學課程的專案，請執行 Maven 命令，該命令會提示您輸入如何設定專案。輸入並確認所有輸入後，Maven 會建立 `pom.xml` 檔案和建立 stub Java 檔案來完成建置專案。

1. 開啟終端機或命令提示視窗，然後導覽至您選擇的目錄，例如您的 `Desktop` 或 `Home` 資料夾。

1. 在終端輸入下列命令，然後按下 **Enter** 鍵。

   ```
   mvn archetype:generate \
      -DarchetypeGroupId=software.amazon.awssdk \
      -DarchetypeArtifactId=archetype-app-quickstart \
      -DarchetypeVersion=2.22.0
   ```

1. 針對每個提示，輸入第二欄中列出的值。    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/amazondynamodb/latest/developerguide/ProgrammingWithJava.html)

1. 輸入最後一個值後，Maven 會列出您所做的選擇。若要確認，請輸入 **Y**。或者輸入 **N**，然後重新輸入您的選擇。

Maven 會根據您輸入的 `artifactId` 值建立名為 `getstarted` 的專案資料夾。在 `getstarted` 資料夾中，尋找名為 `README.md` 的檔案以供檢閱、`pom.xml` 檔案和 `src` 目錄。

Maven 會建置下列目錄樹狀目錄。

```
getstarted
 ├── README.md
 ├── pom.xml
 └── src
     ├── main
     │   ├── java
     │   │   └── org
     │   │       └── example
     │   │           ├── App.java
     │   │           ├── DependencyFactory.java
     │   │           └── Handler.java
     │   └── resources
     │       └── simplelogger.properties
     └── test
         └── java
             └── org
                 └── example
                     └── HandlerTest.java
 
 10 directories, 7 files
```

下列顯示 `pom.xml` 專案檔案的內容。

#### `pom.xml`
<a name="ProjectSetupCollapse2"></a>

`dependencyManagement` 區段包含 AWS SDK for Java 2.x的相依性，而 `dependencies` 區段具有 DynamoDB 的相依性。指定這些相依性會強制 Maven 在 Java 類別路徑中包含相關的 `.jar` 檔案。根據預設， AWS 軟體開發套件不會包含所有 的所有類別 AWS 服務。對於 DynamoDB，如果您使用低階介面，則應具有對 `dynamodb` 成品的相依性。或者，如果您使用高階介面，則為 `dynamodb-enhanced` 成品。如果您未包含相關的相依性，則程式碼無法編譯。由於 `maven.compiler.source` 和 `maven.compiler.target` 屬性中的 `1.8` 值，專案使用 Java 1.8。

```
<?xml version="1.0" encoding="UTF-8"?>
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
 
     <groupId>org.example</groupId>
     <artifactId>getstarted</artifactId>
     <version>1.0-SNAPSHOT</version>
     <packaging>jar</packaging>
     <properties>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <maven.compiler.source>1.8</maven.compiler.source>
         <maven.compiler.target>1.8</maven.compiler.target>
         <maven.shade.plugin.version>3.2.1</maven.shade.plugin.version>
         <maven.compiler.plugin.version>3.6.1</maven.compiler.plugin.version>
         <exec-maven-plugin.version>1.6.0</exec-maven-plugin.version>
         <aws.java.sdk.version>2.22.0</aws.java.sdk.version> <-------- SDK version picked up from archetype version.
         <slf4j.version>1.7.28</slf4j.version>
         <junit5.version>5.8.1</junit5.version>
     </properties>
 
     <dependencyManagement>
         <dependencies>
             <dependency>
                 <groupId>software.amazon.awssdk</groupId>
                 <artifactId>bom</artifactId>
                 <version>${aws.java.sdk.version}</version>
                 <type>pom</type>
                 <scope>import</scope>
             </dependency>
         </dependencies>
     </dependencyManagement>
 
     <dependencies>
         <dependency>
             <groupId>software.amazon.awssdk</groupId>
             <artifactId>dynamodb</artifactId>  <-------- DynamoDB dependency
             <exclusions>
                 <exclusion>
                     <groupId>software.amazon.awssdk</groupId>
                     <artifactId>netty-nio-client</artifactId>
                 </exclusion>
                 <exclusion>
                     <groupId>software.amazon.awssdk</groupId>
                     <artifactId>apache-client</artifactId>
                 </exclusion>
             </exclusions>
         </dependency>
 
         <dependency>
             <groupId>software.amazon.awssdk</groupId>
             <artifactId>sso</artifactId> <-------- Required for identity center authentication.
         </dependency>
 
         <dependency>
             <groupId>software.amazon.awssdk</groupId>
             <artifactId>ssooidc</artifactId> <-------- Required for identity center authentication.
         </dependency>
 
         <dependency>
             <groupId>software.amazon.awssdk</groupId>
             <artifactId>apache-client</artifactId> <-------- HTTP client specified.
             <exclusions>
                 <exclusion>
                     <groupId>commons-logging</groupId>
                     <artifactId>commons-logging</artifactId>
                 </exclusion>
             </exclusions>
         </dependency>
 
         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
             <version>${slf4j.version}</version>
         </dependency>
 
         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-simple</artifactId>
             <version>${slf4j.version}</version>
         </dependency>
 
         <!-- Needed to adapt Apache Commons Logging used by Apache HTTP Client to Slf4j to avoid
         ClassNotFoundException: org.apache.commons.logging.impl.LogFactoryImpl during runtime -->
         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>jcl-over-slf4j</artifactId>
             <version>${slf4j.version}</version>
         </dependency>
 
         <!-- Test Dependencies -->
         <dependency>
             <groupId>org.junit.jupiter</groupId>
             <artifactId>junit-jupiter</artifactId>
             <version>${junit5.version}</version>
             <scope>test</scope>
         </dependency>
     </dependencies>
 
     <build>
         <plugins>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-compiler-plugin</artifactId>
                 <version>${maven.compiler.plugin.version}</version>
             </plugin>
         </plugins>
     </build>
 
 </project>
```

### 步驟 3：撰寫程式碼
<a name="GetStartedJavaCode"></a>

下列程式碼顯示 Maven 建立的 `App` 類別。`main` 方法是應用程式的進入點，它會建立 `Handler` 類別的執行個體，然後呼叫其 `sendRequest` 方法。

#### `App` 類別
<a name="projectsetup-collapse2"></a>

```
package org.example;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 public class App {
     private static final Logger logger = LoggerFactory.getLogger(App.class);
 
     public static void main(String... args) {
         logger.info("Application starts");
 
         Handler handler = new Handler();
         handler.sendRequest();
 
         logger.info("Application ends");
     }
 }
```

Maven 建立的 `DependencyFactory` 類別包含建置和傳回 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html) 執行個體的 `dynamoDbClient` 原廠方法。`DynamoDbClient` 執行個體使用 Apache 型 HTTP 用戶端的執行個體。這是因為您在 Maven 提示您使用哪個 HTTP 用戶端時指定了 `apache-client`。

下列程式碼顯示 `DependencyFactory` 類別。

#### DependencyFactory 類別
<a name="code-collapse2"></a>

```
package org.example;
 
 import software.amazon.awssdk.http.apache.ApacheHttpClient;
 import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
 
 /**
  * The module containing all dependencies required by the {@link Handler}.
  */
 public class DependencyFactory {
 
     private DependencyFactory() {}
 
     /**
      * @return an instance of DynamoDbClient
      */
     public static DynamoDbClient dynamoDbClient() {
         return DynamoDbClient.builder()
                        .httpClientBuilder(ApacheHttpClient.builder())
                        .build();
     }
 }
```

`Handler` 類別包含您程式的主要邏輯。`App` 類別中建立了 `Handler` 的執行個體時，`DependencyFactory` 會提供 `DynamoDbClient` 服務用戶端。您的程式碼使用 `DynamoDbClient` 執行個體來呼叫 DynamoDB。

Maven 會產生具有 `TODO` 註解的下列 `Handler` 類別。教學課程中的下一個步驟會以程式碼取代 *`TODO`* 註解。

#### `Handler` 類別，Maven 產生的
<a name="code-collapsible3"></a>

```
package org.example;
 
 import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
 
 
 public class Handler {
     private final DynamoDbClient dynamoDbClient;
 
     public Handler() {
         dynamoDbClient = DependencyFactory.dynamoDbClient();
     }
 
     public void sendRequest() {
         // TODO: invoking the API calls using dynamoDbClient.
     }
 }
```

若要填入邏輯，請使用下列程式碼取代 `Handler` 類別的整個內容。`sendRequest` 方法已填入，並新增必要的匯入。

#### `Handler` 類別，已實作
<a name="code-collapse4"></a>

下列程式碼使用 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html) 執行個體來擷取現有資料表的清單。如果指定帳戶和 AWS 區域內存在資料表，則程式碼會使用 `Logger` 執行個體來記錄這些資料表的名稱。

```
package org.example;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
 import software.amazon.awssdk.services.dynamodb.model.ListTablesResponse;
 
 
 public class Handler {
     private final DynamoDbClient dynamoDbClient;
 
     public Handler() {
         dynamoDbClient = DependencyFactory.dynamoDbClient();
     }
 
     public void sendRequest() {
         Logger logger = LoggerFactory.getLogger(Handler.class);
 
         logger.info("calling the DynamoDB API to get a list of existing tables");
         ListTablesResponse response = dynamoDbClient.listTables();
 
         if (!response.hasTableNames()) {
             logger.info("No existing tables found for the configured account & region");
         } else {
             response.tableNames().forEach(tableName -> logger.info("Table: " + tableName));
         }
     }
 }
```

### 步驟 4：建置並執行應用程式
<a name="GetStartedRunJava"></a>

在您建立專案並包含完整的 `Handler` 類別之後，請建置並執行應用程式。

1. 請確定您有一個作用中的 AWS IAM Identity Center 工作階段。若要確認，請執行 AWS Command Line Interface (AWS CLI) 命令 `aws sts get-caller-identity` 並檢查回應。如果您沒有作用中的工作階段，請參閱[使用 AWS CLI登入](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/setup.html#setup-login-sso)以取得指示。

1. 開啟終端機或命令提示視窗，然後導覽至您的專案目錄 `getstarted`。

1. 使用下列命令來建置專案：

   ```
   mvn clean package
   ```

1. 若要部署應用程式，請執行下列命令：

   ```
   mvn exec:java -Dexec.mainClass="org.example.App"
   ```

檢視檔案之後，請刪除物件，然後刪除儲存貯體。

#### 成功
<a name="GetStartedSuccessJava"></a>

如果您的 Maven 專案能建置且執行時沒有錯誤，恭喜您！您已成功使用適用於 Java 的 SDK 2.x 建置了第一個 Java 應用程式。

#### 清除
<a name="GetStartedCleanupJava"></a>

若要清除您在本教學課程中建立的資源，請刪除專案資料夾 `getstarted`。

## 檢閱 AWS SDK for Java 2.x 文件
<a name="ProgrammingWithJavaUseDoc"></a>

[AWS SDK for Java 2.x 開發人員指南](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/home.html)涵蓋 SDK 在一切 AWS 服務中的所有層面。建議您檢閱下列主題：
+ [從 1.x 版移轉至 2.x](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/migration.html) – 包含 1.x 和 2.x 之間差異的詳細說明。本主題也包含如何使用兩個主要版本的指示，以並排方式顯示。
+ [適用於 Java 2.x SDK 的 DynamoDB 指南](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/examples-dynamodb.html) – 說明如何執行基本的 DynamoDB 操作：建立資料表、操作項目和擷取項目。這些範例使用低階介面。Java 有數個介面，如下節所述：[支援的介面](#JavaInterfaces)。

**提示**  
檢閱這些主題之後，請將 [AWS SDK for Java 2.x API 參考](https://sdk.amazonaws.com/java/api/latest/)加入書籤。它涵蓋所有項目 AWS 服務，我們建議您將其用作主要 API 參考。

## 支援的介面
<a name="JavaInterfaces"></a>

 AWS SDK for Java 2.x 支援下列界面，視您想要的抽象程度而定。

**Topics**
+ [低階介面](#LowLevelInterface)
+ [高階介面](#HighLevelInterface)
+ [文件介面](#DocumentInterface)
+ [使用 `Query` 範例比較各介面](#CompareJavaInterfacesQueryEx)

### 低階介面
<a name="LowLevelInterface"></a>

低階介面提供對基礎服務 API 的 1 對 1 映射。每個 DynamoDB API 都可以透過此介面使用。這表示低階介面可以提供完整的功能，但通常使用起來更冗長且複雜。例如，您必須使用 `.s()` 函數來保留字串，並使用 `.n()` 函數來保留數字。下列 [PutItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_PutItem.html) 範例使用低階介面插入項目。

```
import org.slf4j.*;
import software.amazon.awssdk.http.crt.AwsCrtHttpClient;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.*;

import java.util.Map;

public class PutItem {

    // Create a DynamoDB client with the default settings connected to the DynamoDB
    // endpoint in the default region based on the default credentials provider chain.
    private static final DynamoDbClient DYNAMODB_CLIENT = DynamoDbClient.create();
    private static final Logger LOGGER = LoggerFactory.getLogger(PutItem.class);

    private void putItem() {
        PutItemResponse response = DYNAMODB_CLIENT.putItem(PutItemRequest.builder()
                .item(Map.of(
                        "pk", AttributeValue.builder().s("123").build(),
                        "sk", AttributeValue.builder().s("cart#123").build(),
                        "item_data", AttributeValue.builder().s("YourItemData").build(),
                        "inventory", AttributeValue.builder().n("500").build()
                        // ... more attributes ...
                ))
                .returnConsumedCapacity(ReturnConsumedCapacity.TOTAL)
                .tableName("YourTableName")
                .build());
        LOGGER.info("PutItem call consumed [" + response.consumedCapacity().capacityUnits() + "] Write Capacity Unites (WCU)");
    }
}
```

### 高階介面
<a name="HighLevelInterface"></a>

中的高階界面 AWS SDK for Java 2.x 稱為 DynamoDB 增強型用戶端。此介面提供更道地的程式碼撰寫體驗。

增強型用戶端提供一種在用戶端資料類別和旨在存放該資料的 DynamoDB 資料表之間映射的方法。您要在自己的程式碼中定義資料表及其對應模型類別之間的關係。然後，您可以依賴 SDK 來管理資料類型處理。如需增強型用戶端的詳細資訊，請參閱《*AWS SDK for Java 2.x 開發人員指南*》中的 [DynamoDB 增強型用戶端 API](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/dynamodb-enhanced-client.html)。

下列 [PutItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_PutItem.html) 範例使用高階介面。在此範例中，名為 `YourItem` 的 `DynamoDbBean` 會建立 `TableSchema`，使其能夠直接使用為 `putItem()` 呼叫的輸入。

```
import org.slf4j.*;
import software.amazon.awssdk.enhanced.dynamodb.*;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.*;
import software.amazon.awssdk.enhanced.dynamodb.model.*;
import software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity;

public class DynamoDbEnhancedClientPutItem {
    private static final DynamoDbEnhancedClient ENHANCED_DYNAMODB_CLIENT = DynamoDbEnhancedClient.builder().build();
    private static final DynamoDbTable<YourItem> DYNAMODB_TABLE = ENHANCED_DYNAMODB_CLIENT.table("YourTableName", TableSchema.fromBean(YourItem.class));
    private static final Logger LOGGER = LoggerFactory.getLogger(PutItem.class);

    private void putItem() {
        PutItemEnhancedResponse<YourItem> response = DYNAMODB_TABLE.putItemWithResponse(PutItemEnhancedRequest.builder(YourItem.class)
                .item(new YourItem("123", "cart#123", "YourItemData", 500))
                .returnConsumedCapacity(ReturnConsumedCapacity.TOTAL)
                .build());
        LOGGER.info("PutItem call consumed [" + response.consumedCapacity().capacityUnits() + "] Write Capacity Unites (WCU)");
    }

    @DynamoDbBean
    public static class YourItem {

        public YourItem() {}

        public YourItem(String pk, String sk, String itemData, int inventory) {
            this.pk = pk;
            this.sk = sk;
            this.itemData = itemData;
            this.inventory = inventory;
        }

        private String pk;
        private String sk;
        private String itemData;

        private int inventory;

        @DynamoDbPartitionKey
        public void setPk(String pk) {
            this.pk = pk;
        }

        public String getPk() {
            return pk;
        }

        @DynamoDbSortKey
        public void setSk(String sk) {
            this.sk = sk;
        }

        public String getSk() {
            return sk;
        }

        public void setItemData(String itemData) {
            this.itemData = itemData;
        }

        public String getItemData() {
            return itemData;
        }

        public void setInventory(int inventory) {
            this.inventory = inventory;
        }

        public int getInventory() {
            return inventory;
        }
    }
}
```

 適用於 Java 的 AWS SDK 1.x 有自己的高階界面，通常由其主要類別 參考。 `DynamoDBMapper` AWS SDK for Java 2.x 會以名為 的個別套件 （和 Maven 成品） 發佈`software.amazon.awssdk.enhanced.dynamodb`。Java 2.x SDK 通常由其主類別 `DynamoDbEnhancedClient` 參考。

#### 使用不可變資料類別的高階介面
<a name="HighLevelInterfaceImmutableDataClasses"></a>

DynamoDB 增強型用戶端 API 的映射功能也適用於不可變的資料類別。不可變類別只有取得器，且需要 SDK 用來建立類別執行個體的建置器類別。Java 中的不可變性是一種常用的樣式，開發人員可用來建立沒有副作用的類別。這些類別在複雜多執行緒應用程式中的行為更可預測。不可變類別會使用 `@DynamoDbImmutable` 註釋，其採用建置器類別做為其輸入，而不是使用 [High-level interface example](#highleveleg) 中所示的 `@DynamoDbBean` 註釋。

下列範例採用建置器類別 `DynamoDbEnhancedClientImmutablePutItem` 做為輸入來建立資料表結構描述。然後，此範例提供結構描述做為 [PutItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_PutItem.html) API 呼叫的輸入。

```
import org.slf4j.*;
import software.amazon.awssdk.enhanced.dynamodb.*;
import software.amazon.awssdk.enhanced.dynamodb.model.*;
import software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity;

public class DynamoDbEnhancedClientImmutablePutItem {
    private static final DynamoDbEnhancedClient ENHANCED_DYNAMODB_CLIENT = DynamoDbEnhancedClient.builder().build();
    private static final DynamoDbTable<YourImmutableItem> DYNAMODB_TABLE = ENHANCED_DYNAMODB_CLIENT.table("YourTableName", TableSchema.fromImmutableClass(YourImmutableItem.class));
    private static final Logger LOGGER = LoggerFactory.getLogger(DynamoDbEnhancedClientImmutablePutItem.class);

    private void putItem() {
        PutItemEnhancedResponse<YourImmutableItem> response = DYNAMODB_TABLE.putItemWithResponse(PutItemEnhancedRequest.builder(YourImmutableItem.class)
                .item(YourImmutableItem.builder()
                                        .pk("123")
                                        .sk("cart#123")
                                        .itemData("YourItemData")
                                        .inventory(500)
                                        .build())
                .returnConsumedCapacity(ReturnConsumedCapacity.TOTAL)
                .build());
        LOGGER.info("PutItem call consumed [" + response.consumedCapacity().capacityUnits() + "] Write Capacity Unites (WCU)");
    }
}
```

下列範例顯示不可變的資料類別。

```
@DynamoDbImmutable(builder = YourImmutableItem.YourImmutableItemBuilder.class)
class YourImmutableItem {
    private final String pk;
    private final String sk;
    private final String itemData;
    private final int inventory;
    public YourImmutableItem(YourImmutableItemBuilder builder) {
        this.pk = builder.pk;
        this.sk = builder.sk;
        this.itemData = builder.itemData;
        this.inventory = builder.inventory;
    }

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

    @DynamoDbPartitionKey
    public String getPk() {
        return pk;
    }

    @DynamoDbSortKey
    public String getSk() {
        return sk;
    }

    public String getItemData() {
        return itemData;
    }

    public int getInventory() {
        return inventory;
    }

    static final class YourImmutableItemBuilder {
        private String pk;
        private String sk;
        private String itemData;
        private int inventory;

        private YourImmutableItemBuilder() {}

        public YourImmutableItemBuilder pk(String pk) { this.pk = pk; return this; }
        public YourImmutableItemBuilder sk(String sk) { this.sk = sk; return this; }
        public YourImmutableItemBuilder itemData(String itemData) { this.itemData = itemData; return this; }
        public YourImmutableItemBuilder inventory(int inventory) { this.inventory = inventory; return this; }

        public YourImmutableItem build() { return new YourImmutableItem(this); }
    }
}
```

#### 使用不可變資料類別和第三方樣板產生程式庫的高階介面
<a name="ImmutableDataClassesThirdPartyBoilerplateGenLib"></a>

不可變的資料類別 (如上例所示) 需要一些樣板程式碼。例如，除了 `Builder` 類別之外，對資料類別的取得器和設定器邏輯。例如 [Project Lombok](https://projectlombok.org/) 等第三方程式庫可協助您產生該類型的樣板程式碼。減少大部分樣板程式碼可協助您限制使用不可變資料類別和 AWS SDK 所需的程式碼數量。這進一步提高了生產力與程式碼的可讀性。如需詳細資訊，請參閱《*AWS SDK for Java 2.x 開發人員指南*》中的[使用 Lombok 等第三方程式庫](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/ddb-en-client-use-immut.html#ddb-en-client-use-immut-lombok)。

下列範例示範 Project Lombok 如何簡化使用 DynamoDB 增強型用戶端 API 所需的程式碼。

```
import org.slf4j.*;
import software.amazon.awssdk.enhanced.dynamodb.*;
import software.amazon.awssdk.enhanced.dynamodb.model.*;
import software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity;

public class DynamoDbEnhancedClientImmutableLombokPutItem {

    private static final DynamoDbEnhancedClient ENHANCED_DYNAMODB_CLIENT = DynamoDbEnhancedClient.builder().build();
    private static final DynamoDbTable<YourImmutableLombokItem> DYNAMODB_TABLE = ENHANCED_DYNAMODB_CLIENT.table("YourTableName", TableSchema.fromImmutableClass(YourImmutableLombokItem.class));
    private static final Logger LOGGER = LoggerFactory.getLogger(DynamoDbEnhancedClientImmutableLombokPutItem.class);

    private void putItem() {
        PutItemEnhancedResponse<YourImmutableLombokItem> response = DYNAMODB_TABLE.putItemWithResponse(PutItemEnhancedRequest.builder(YourImmutableLombokItem.class)
                .item(YourImmutableLombokItem.builder()
                        .pk("123")
                        .sk("cart#123")
                        .itemData("YourItemData")
                        .inventory(500)
                        .build())
                .returnConsumedCapacity(ReturnConsumedCapacity.TOTAL)
                .build());
        LOGGER.info("PutItem call consumed [" + response.consumedCapacity().capacityUnits() + "] Write Capacity Unites (WCU)");
    }
}
```

下列範例顯示不可變資料類別的不可變資料物件。

```
import lombok.*;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.*;

@Builder
@DynamoDbImmutable(builder = YourImmutableLombokItem.YourImmutableLombokItemBuilder.class)
@Value
public class YourImmutableLombokItem {

    @Getter(onMethod_=@DynamoDbPartitionKey)
    String pk;
    @Getter(onMethod_=@DynamoDbSortKey)
    String sk;
    String itemData;
    int inventory;
}
```

`YourImmutableLombokItem` 類別使用 Project Lombok 和 AWS SDK 提供的下列註釋：
+ [@Builder](https://projectlombok.org/features/Builder) – 為 Project Lombok 提供的資料類別產生複雜的建置器 API。
+ [@DynamoDbImmutable](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/annotations/DynamoDbImmutable.html) – 將`DynamoDbImmutable`類別識別為 AWS 開發套件提供的 DynamoDB 可映射實體註釋。
+ [@Value](https://projectlombok.org/features/Value) – `@Data` 的不可變變體。根據預設，所有欄位都是私有和最終欄位，不會產生設定器。Project Lombok 提供此註釋。

### 文件介面
<a name="DocumentInterface"></a>

 AWS SDK for Java 2.x 文件界面不需要指定資料類型描述項。資料類型是由資料本身的語義隱含的。此文件界面類似於 文件界面 適用於 Java 的 AWS SDK 1.x，但具有重新設計的界面。

以下 [Document interface example](#DocInterfaceEg) 顯示使用文件介面表示的 `PutItem` 呼叫。此範例也使用 EnhancedDocument。若要使用增強型文件 API 針對 DynamoDB 資料表執行命令，您必須先將資料表與文件資料表結構描述建立關聯，才能建立 `DynamoDBTable` 資源物件。文件資料表結構描述建置器需要主索引鍵和屬性轉換器提供者。

您可以使用 `AttributeConverterProvider.defaultProvider()` 來轉換預設類型的文件屬性。您可以使用自訂 `AttributeConverterProvider` 實作變更整體預設行為。您也可以變更單一屬性的轉換器。[AWS SDK 和工具參考指南](https://docs.aws.amazon.com/sdkref/latest/guide/version-support-matrix.html)提供有關如何使用自訂轉換器的更多詳細資訊和範例。其主要用途是用於沒有預設轉換器的網域類別屬性。使用自訂轉換器，您可以向 SDK 提供寫入或讀取至 DynamoDB 所需的資訊。

```
import org.slf4j.*;
import software.amazon.awssdk.enhanced.dynamodb.*;
import software.amazon.awssdk.enhanced.dynamodb.document.EnhancedDocument;
import software.amazon.awssdk.enhanced.dynamodb.model.*;
import software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity;

public class DynamoDbEnhancedDocumentClientPutItem {
    private static final DynamoDbEnhancedClient ENHANCED_DYNAMODB_CLIENT = DynamoDbEnhancedClient.builder().build();
    private static final DynamoDbTable<EnhancedDocument> DYNAMODB_TABLE =
            ENHANCED_DYNAMODB_CLIENT.table("YourTableName", TableSchema.documentSchemaBuilder()
                            .addIndexPartitionKey(TableMetadata.primaryIndexName(),"pk", AttributeValueType.S)
                            .addIndexSortKey(TableMetadata.primaryIndexName(), "sk", AttributeValueType.S)
                            .attributeConverterProviders(AttributeConverterProvider.defaultProvider())
                            .build());

    private static final Logger LOGGER = LoggerFactory.getLogger(DynamoDbEnhancedDocumentClientPutItem.class);

    private void putItem() {
        PutItemEnhancedResponse<EnhancedDocument> response = DYNAMODB_TABLE.putItemWithResponse(
                        PutItemEnhancedRequest.builder(EnhancedDocument.class)
                                .item(
                                    EnhancedDocument.builder()
                                            .attributeConverterProviders(AttributeConverterProvider.defaultProvider())
                                            .putString("pk", "123")
                                            .putString("sk", "cart#123")
                                            .putString("item_data", "YourItemData")
                                            .putNumber("inventory", 500)
                                            .build())
                                .returnConsumedCapacity(ReturnConsumedCapacity.TOTAL)
                                .build());
        LOGGER.info("PutItem call consumed [" + response.consumedCapacity().capacityUnits() + "] Write Capacity Unites (WCU)");
    }

}
```

若要將原生 Amazon DynamoDB 資料類型與 JSON 文件彼此轉換，您可以使用下列公用程式方法：
+ [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/document/EnhancedDocument.html#fromJson(java.lang.String)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/document/EnhancedDocument.html#fromJson(java.lang.String)) – 從 JSON 字串建立新的 EnhancedDocument 實體。
+ [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/document/EnhancedDocument.html#toJson()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/document/EnhancedDocument.html#toJson()) – 建立文件的 JSON 字串表示法，您可以在應用程式中使用，就像任何其他 JSON 物件一樣。

### 使用 `Query` 範例比較各介面
<a name="CompareJavaInterfacesQueryEx"></a>

本節顯示使用各種介面表示的相同 [https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html) 呼叫。若要微調這些查詢的結果，請注意下列事項：
+ DynamoDB 以一個特定的分割區索引鍵值為目標，因此您必須完全指定分割區索引鍵。
+ 若要讓查詢目標僅購物車項目，排序索引鍵具有使用 `begins_with` 的索引鍵條件表達式。
+ 我們使用 `limit()` 將查詢限制為最多傳回 100 個項目。
+ 我們將 `scanIndexForward` 設定為 false。結果會依 UTF-8 位元組的順序傳回，這通常表示會先傳回數量最低的購物車項目。透過將 `scanIndexForward` 設定為 false，我們把順序反轉，並先傳回數量最高的購物車項目。
+ 我們會套用篩選條件，以移除不符合條件的任何結果。篩選的資料會耗用讀取容量，無論項目是否符合篩選條件。

**Example `Query` 使用低階介面**  
下列範例使用 `keyConditionExpression` 查詢名為 `YourTableName` 的資料表。這會將查詢限制為特定的分割區索引鍵值，以及以特定字首值開頭的排序索引鍵值。這些金鑰條件會限制從 DynamoDB 讀取的資料量。最後，查詢會對使用 `filterExpression` 從 DynamoDB 擷取的資料套用篩選條件。  

```
import org.slf4j.*;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.*;

import java.util.Map;

public class Query {

    // Create a DynamoDB client with the default settings connected to the DynamoDB 
    // endpoint in the default region based on the default credentials provider chain.
    private static final DynamoDbClient DYNAMODB_CLIENT = DynamoDbClient.builder().build();
    private static final Logger LOGGER = LoggerFactory.getLogger(Query.class);

    private static void query() {
        QueryResponse response = DYNAMODB_CLIENT.query(QueryRequest.builder()
                .expressionAttributeNames(Map.of("#name", "name"))
                .expressionAttributeValues(Map.of(
                    ":pk_val", AttributeValue.fromS("id#1"),
                    ":sk_val", AttributeValue.fromS("cart#"),
                    ":name_val", AttributeValue.fromS("SomeName")))
                .filterExpression("#name = :name_val")
                .keyConditionExpression("pk = :pk_val AND begins_with(sk, :sk_val)")
                .limit(100)
                .scanIndexForward(false)
                .tableName("YourTableName")
                .build());

        LOGGER.info("nr of items: " + response.count());
        LOGGER.info("First item pk: " + response.items().get(0).get("pk"));
        LOGGER.info("First item sk: " + response.items().get(0).get("sk"));
    }
}
```

**Example `Query` 使用文件介面**  
下列範例會使用文件介面查詢名為 `YourTableName` 的資料表。  

```
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.enhanced.dynamodb.*;
import software.amazon.awssdk.enhanced.dynamodb.document.EnhancedDocument;
import software.amazon.awssdk.enhanced.dynamodb.model.*;

import java.util.Map;

public class DynamoDbEnhancedDocumentClientQuery {

    // Create a DynamoDB client with the default settings connected to the DynamoDB 
    // endpoint in the default region based on the default credentials provider chain.
    private static final DynamoDbEnhancedClient ENHANCED_DYNAMODB_CLIENT = DynamoDbEnhancedClient.builder().build();
    private static final DynamoDbTable<EnhancedDocument> DYNAMODB_TABLE =
            ENHANCED_DYNAMODB_CLIENT.table("YourTableName", TableSchema.documentSchemaBuilder()
                    .addIndexPartitionKey(TableMetadata.primaryIndexName(),"pk", AttributeValueType.S)
                    .addIndexSortKey(TableMetadata.primaryIndexName(), "sk", AttributeValueType.S)
                    .attributeConverterProviders(AttributeConverterProvider.defaultProvider())
                    .build());
    private static final Logger LOGGER = LoggerFactory.getLogger(DynamoDbEnhancedDocumentClientQuery.class);

    private void query() {
        PageIterable<EnhancedDocument> response = DYNAMODB_TABLE.query(QueryEnhancedRequest.builder()
                .filterExpression(Expression.builder()
                        .expression("#name = :name_val")
                        .expressionNames(Map.of("#name", "name"))
                        .expressionValues(Map.of(":name_val", AttributeValue.fromS("SomeName")))
                        .build())
                .limit(100)
                .queryConditional(QueryConditional.sortBeginsWith(Key.builder()
                        .partitionValue("id#1")
                        .sortValue("cart#")
                        .build()))
                .scanIndexForward(false)
                .build());

        LOGGER.info("nr of items: " + response.items().stream().count());
        LOGGER.info("First item pk: " + response.items().iterator().next().getString("pk"));
        LOGGER.info("First item sk: " + response.items().iterator().next().getString("sk"));

    }
}
```

**Example `Query` 使用高階介面**  
下列範例會使用 DynamoDB 增強型用戶端 API 查詢名為 `YourTableName` 的資料表。  

```
import org.slf4j.*;
import software.amazon.awssdk.enhanced.dynamodb.*;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.*;
import software.amazon.awssdk.enhanced.dynamodb.model.*;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;

import java.util.Map;

public class DynamoDbEnhancedClientQuery {

    private static final DynamoDbEnhancedClient ENHANCED_DYNAMODB_CLIENT = DynamoDbEnhancedClient.builder().build();
    private static final DynamoDbTable<YourItem> DYNAMODB_TABLE = ENHANCED_DYNAMODB_CLIENT.table("YourTableName", TableSchema.fromBean(DynamoDbEnhancedClientQuery.YourItem.class));
    private static final Logger LOGGER = LoggerFactory.getLogger(DynamoDbEnhancedClientQuery.class);

    private void query() {
        PageIterable<YourItem> response = DYNAMODB_TABLE.query(QueryEnhancedRequest.builder()
                .filterExpression(Expression.builder()
                        .expression("#name = :name_val")
                        .expressionNames(Map.of("#name", "name"))
                        .expressionValues(Map.of(":name_val", AttributeValue.fromS("SomeName")))
                        .build())
                .limit(100)
                .queryConditional(QueryConditional.sortBeginsWith(Key.builder()
                        .partitionValue("id#1")
                        .sortValue("cart#")
                        .build()))
                .scanIndexForward(false)
                .build());

        LOGGER.info("nr of items: " + response.items().stream().count());
        LOGGER.info("First item pk: " + response.items().iterator().next().getPk());
        LOGGER.info("First item sk: " + response.items().iterator().next().getSk());
    }

    @DynamoDbBean
    public static class YourItem {

        public YourItem() {}

        public YourItem(String pk, String sk, String name) {
            this.pk = pk;
            this.sk = sk;
            this.name = name;
        }

        private String pk;
        private String sk;
        private String name;

        @DynamoDbPartitionKey
        public void setPk(String pk) {
            this.pk = pk;
        }

        public String getPk() {
            return pk;
        }

        @DynamoDbSortKey
        public void setSk(String sk) {
            this.sk = sk;
        }

        public String getSk() {
            return sk;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }
    }
}
```
**使用不可變資料類別的高階介面**  
當您使用高階不可變資料類別執行 `Query` 時，程式碼會與高階介面範例相同，但實體類別 `YourItem` 或 `YourImmutableItem` 的建構除外。如需詳細資訊，請參閱 [PutItem](#HighLevelImmutableDataClassEg) 範例。
**使用不可變資料類別和第三方樣板產生程式庫的高階介面**  
當您使用高階不可變資料類別執行 `Query` 時，程式碼會與高階介面範例相同，但實體類別 `YourItem` 或 `YourImmutableLombokItem` 的建構除外。如需詳細資訊，請參閱 [PutItem](#HighLevelImmutableDataClassEg) 範例。

## 其他程式碼範例
<a name="AdditionalCodeEx"></a>

如需如何搭配適用於 Java 2.x 的 SDK 使用 DynamoDB 的其他範例，請參閱下列程式碼範例儲存庫：
+ [官方 AWS 單一動作程式碼範例](https://docs.aws.amazon.com/code-library/latest/ug/java_2_dynamodb_code_examples.html)
+ [社群維護的單一動作程式碼範例](https://github.com/aws-samples/aws-dynamodb-examples/tree/master/examples/SDK/java)
+ [官方 AWS 案例導向程式碼範例](https://github.com/aws-samples/aws-dynamodb-examples/tree/master/examples/SDK/java)

## 同步和非同步程式設計
<a name="SyncAsyncProgramming"></a>

為 AWS SDK for Java 2.x 提供*同步*和非*同步*用戶端 AWS 服務，例如 DynamoDB。

`DynamoDbClient` 和 `DynamoDbEnhancedClient` 類別提供同步方法，這些方法會封鎖您的執行緒執行，直到用戶端收到服務的回應。如果您不需要非同步操作，此用戶端是與 DynamoDB 互動最直接的方式。

`DynamoDbAsyncClient` 和 `DynamoDbEnhancedAsyncClient` 類別提供非同步方法，這些方法會立即傳回，將控制權交還給呼叫端執行緒，無需等待回應。非封鎖用戶端具有優勢，可在幾個執行緒中用於獲得高並行，以最少的運算資源有效率地處理 I/O 請求。這可改善輸送量和回應能力。

 AWS SDK for Java 2.x 使用原生支援進行非封鎖 I/O。 適用於 Java 的 AWS SDK 1.x 必須模擬非封鎖 I/O。

同步方法會在有可用回應時傳回結果，您需要一個方法在回應準備好時取得回應。中的非同步方法會 適用於 Java 的 AWS SDK 傳回[https://docs.oracle.com/javase/8/docs/api/index.html?java/util/concurrent/CompletableFuture.html](https://docs.oracle.com/javase/8/docs/api/index.html?java/util/concurrent/CompletableFuture.html)物件，其中包含未來非同步操作的結果。當您對這些 `CompletableFuture` 物件呼叫 `get()` 或 `join()` 時，您的程式碼會封鎖，直到結果可用為止。如果您在提出請求的同時呼叫這些函數，則行為類似於純同步呼叫。

如需非同步程式設計的詳細資訊，請參閱《*AWS SDK for Java 2.x 開發人員指南*》中的[使用非同步程式設計](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/asynchronous.html)。

## HTTP 用戶端
<a name="HttpClients"></a>

為了支援每個用戶端，有一個 HTTP 用戶端來處理與 AWS 服務的通訊。您可以插入替代 HTTP 用戶端，選擇具有最符合您應用程式特性的用戶端。有些更輕量；有些則有更多組態選項。

有些 HTTP 用戶端僅支援同步使用，有些僅支援非同步使用。如需可協助您為工作負載選取最佳 HTTP 用戶端的流程圖，請參閱《*AWS SDK for Java 2.x 開發人員指南*》中的 [HTTP 用戶端建議](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/http-configuration.html#http-clients-recommend)。

下列清單顯示一些可能的 HTTP 用戶端：

**Topics**
+ [Apache 型 HTTP 用戶端](#ApacheHttpClient)
+ [`URLConnection` 型 HTTP 用戶端](#URLConnHttpClient)
+ [Netty 型 HTTP 用戶端](#NettyHttpClient)
+ [AWS CRT 型 HTTP 用戶端](#AWSCRTHttpClient)

### Apache 型 HTTP 用戶端
<a name="ApacheHttpClient"></a>

[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/apache/ApacheHttpClient.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/apache/ApacheHttpClient.html) 類別支援同步服務用戶端。這是同步使用的預設 HTTP 用戶端。如需有關 `ApacheHttpClient` 類別的資訊，請參閱《*AWS SDK for Java 2.x 開發人員指南*》中的[設定 Apache 型 HTTP 用戶端](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/http-configuration-apache.html)。

### `URLConnection` 型 HTTP 用戶端
<a name="URLConnHttpClient"></a>

[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/urlconnection/UrlConnectionHttpClient.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/urlconnection/UrlConnectionHttpClient.html) 類別是同步用戶端的另一個選項。它比 Apache 型 HTTP 用戶端載入更快，但功能較少。如需有關設定 `UrlConnectionHttpClient` 類別的資訊，請參閱《*AWS SDK for Java 2.x 開發人員指南*》中的[設定 URLConnection 型 HTTP 用戶端](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/http-configuration-url.html)。

### Netty 型 HTTP 用戶端
<a name="NettyHttpClient"></a>

`NettyNioAsyncHttpClient` 類別支援非同步用戶端。這是非同步使用的預設選擇。如需有關 `NettyNioAsyncHttpClient` 類別的資訊，請參閱《*AWS SDK for Java 2.x 開發人員指南*》中的[設定 Netty 型 HTTP 用戶端](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/http-configuration-netty.html)。

### AWS CRT 型 HTTP 用戶端
<a name="AWSCRTHttpClient"></a>

 AWS 通用執行期 (CRT) 程式庫中較新的 `AwsCrtHttpClient` 和 `AwsCrtAsyncHttpClient`類別是支援同步和非同步用戶端的更多選項。相較於其他 HTTP 用戶端， AWS CRT 提供：
+ 更快速的 SDK 啟動時間
+ 記憶體佔用空間較小
+ 延遲時間縮短
+ 連線運作狀態管理
+ DNS 負載平衡

如需有關設定 `AwsCrtHttpClient`和 `AwsCrtAsyncHttpClient`類別的資訊，請參閱《 *AWS SDK for Java 2.x 開發人員指南*》中的[設定 AWS CRT 型 HTTP 用戶端](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/http-configuration-crt.html)。

 AWS CRT 型 HTTP 用戶端不是預設值，因為這會破壞現有應用程式的回溯相容性。不過，對於 DynamoDB，我們建議您將 AWS CRT 型 HTTP 用戶端用於同步和非同步用途。

如需 AWS CRT 型 HTTP 用戶端的簡介，請參閱《 *AWS 開發人員工具部落格*[》中的宣布 AWS CRT HTTP 用戶端的可用性 AWS SDK for Java 2.x](https://aws.amazon.com/blogs/developer/announcing-availability-of-the-aws-crt-http-client-in-the-aws-sdk-for-java-2-x/)。

## 設定 HTTP 用戶端
<a name="ConfigHttpClient"></a>

設定用戶端時，您可以提供各種組態選項，包括：
+ 設定 API 呼叫不同層面的逾時。
+ 啟用 TCP Keep-Alive。
+ 遇到錯誤時控制重試政策。
+ 指定[執行攔截器](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/interceptors.html)執行個體可以修改的執行屬性。執行攔截器可以編寫程式碼來攔截 API 請求和回應的執行。這可讓您即時執行發布指標和修改傳輸中請求等任務。
+ 新增或操作 HTTP 標頭。
+ 啟用[用戶端效能指標](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/metrics.html)的追蹤。使用此功能可協助您收集應用程式中的服務用戶端指標並在 Amazon CloudWatch 中分析輸出。
+ 指定要用於排程任務的替代執行器服務，例如非同步重試嘗試和逾時任務。

您可以提供 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/client/config/ClientOverrideConfiguration.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/client/config/ClientOverrideConfiguration.html) 物件給服務用戶端 `Builder` 類別以控制組態。您會在下列各節的一些程式碼範例中看到此情況。

`ClientOverrideConfiguration` 提供標準組態選擇。不同的可插入 HTTP 用戶端也有實作特定的組態可能性。

**Topics**
+ [逾時組態](#TimeoutConfig)
+ [RetryMode](#RetryMode)
+ [DefaultsMode](#DefaultsMode)
+ [Keep-Alive 組態](#KeepAliveConfig)

### 逾時組態
<a name="TimeoutConfig"></a>

您可以調整用戶端組態，以控制與服務呼叫相關的各種逾時。與其他 AWS 服務相比，DynamoDB 提供較低的延遲。因此，您可能想要將這些屬性調整為較低的逾時值，以便在發生聯網問題時快速檢錯。

您可以在 DynamoDB 用戶端上使用 `ClientOverrideConfiguration`，或針對基礎 HTTP 用戶端實作變更詳細的組態選項，以自訂延遲相關行為。

您可以使用 `ClientOverrideConfiguration` 來設定下列具影響力的屬性：
+ `apiCallAttemptTimeout` – 在放棄和逾時之前，等待 HTTP 請求完成一次嘗試的時間長度。
+ `apiCallTimeout` – 用戶端完全執行 API 呼叫所需的時間量。這包括由所有 HTTP 請求組成的請求處理常式執行，包括重試。

 AWS SDK for Java 2.x 提供一些逾時選項的[預設值](https://github.com/aws/aws-sdk-java-v2/blob/a0c8a0af1fa572b16b5bd78f310594d642324156/http-client-spi/src/main/java/software/amazon/awssdk/http/SdkHttpConfigurationOption.java#L134)，例如連線逾時和通訊端逾時。SDK 不提供 API 呼叫逾時或個別 API 呼叫嘗試逾時的預設值。如果未在 `ClientOverrideConfiguration` 中設定這些逾時，則 SDK 會使用通訊端逾時值，而非整體 API 呼叫逾時。通訊端的預設逾時值為 30 秒。

### RetryMode
<a name="RetryMode"></a>

另一個與您應該考慮的逾時組態相關的組態是 `RetryMode` 組態物件。此組態物件包含重試行為的集合。

適用於 Java 2.x 的 SDK 支援下列重試模式：
+ `legacy` – 如果您未明確變更，預設的重試模式。此重試模式專屬於 Java SDK。它的特徵是最多 3 次重試，或對於 DynamoDB 等服務有 8 次重試。
+ `standard` – 命名為「標準」，因為它更符合其他 AWS SDKs。此模式會等待從 0 毫秒到 1,000 毫秒的隨機時間量進行第一次重試。如果需要重試，則此模式會挑選從 0 毫秒到 1,000 毫秒的另一個隨機時間，並將其乘以 2。如果需要額外的重試，則會執行相同的隨機挑選值乘以 4，依此類推。每次等待上限為 20 秒。相較於 `legacy` 模式，此模式在偵測到時會執行重試的失敗條件更多。對於 DynamoDB，除非您使用 [numRetries](#numRetries) 覆寫，否則它最多會執行總共 3 次嘗試。
+ `adaptive` – 以 `standard` 模式為基礎，並動態限制 AWS 請求率，使成功率最大化。這可能會因為請求延遲而發生。當可預測延遲很重要時，我們不建議使用自適應重試模式。

您可以在 *AWS SDK 和工具參考指南*的[重試行為](https://docs.aws.amazon.com/sdkref/latest/guide/feature-retry-behavior.html)主題中找到這些重試模式的擴展定義。

#### 重試政策
<a name="RetryPolicies"></a>

所有 `RetryMode` 組態都有以一或多個 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/conditions/RetryCondition.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/conditions/RetryCondition.html) 組態為基礎的 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/RetryPolicy.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/RetryPolicy.html)。對於 DynamoDB SDK 用戶端實作的重試行為而言，[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/conditions/TokenBucketRetryCondition.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/conditions/TokenBucketRetryCondition.html) 特別重要。此條件會限制 SDK 使用權杖儲存貯體演算法進行的重試次數。根據選取的重試模式，限流例外狀況可能會也可能不會從 `TokenBucket` 中減去權杖。

當用戶端遇到可重試的錯誤，例如限流例外狀況或暫時性伺服器錯誤時，SDK 會自動重試請求。您可以控制這些重試的次數和速度。

設定用戶端時，您可以提供支援下列參數的 `RetryPolicy`：
+ `numRetries` – 請求視為失敗之前應套用的重試次數上限。無論您使用的重試模式為何，預設值都是 8。
**警告**  
請務必在適當考量後變更此預設值。
+ `backoffStrategy` – 要套用至重試的 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/backoff/BackoffStrategy.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/backoff/BackoffStrategy.html)，預設策略為 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/backoff/FullJitterBackoffStrategy.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/backoff/FullJitterBackoffStrategy.html)。此策略會根據目前的次數或重試次數、基本延遲和最大退避時間，在額外重試之間執行指數延遲。然後，它會新增抖動以提供一點隨機性。無論重試模式為何，指數延遲中使用的基本延遲為 25 毫秒。
+ `retryCondition` – [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/conditions/RetryCondition.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/conditions/RetryCondition.html) 決定要不要重試請求。根據預設，它會重試一組特定的 HTTP 狀態碼，以及其認為可重試的例外狀況。在大多數情況下，預設組態應已足夠。

下列程式碼提供替代的重試政策。它總共指定 5 次重試 (總共 6 個請求)。第一次重試應該在延遲大約 100 毫秒之後進行，每次額外的重試都會將該時間以指數方式倍增，最多延遲一秒鐘。

```
DynamoDbClient client = DynamoDbClient.builder()
    .overrideConfiguration(ClientOverrideConfiguration.builder()
        .retryPolicy(RetryPolicy.builder()
            .backoffStrategy(FullJitterBackoffStrategy.builder()
                .baseDelay(Duration.ofMillis(100))
                .maxBackoffTime(Duration.ofSeconds(1))
                .build())
            .numRetries(5)
            .build())
        .build())
    .build();
```

### DefaultsMode
<a name="DefaultsMode"></a>

`ClientOverrideConfiguration` 和 `RetryMode` 不管理的逾時屬性通常透過指定 `DefaultsMode` 來隱含設定。

 AWS SDK for Java 2.x (2.17.102 版或更新版本） 推出對 的支援`DefaultsMode`。此功能為常見的可配置設定提供一組預設值，例如 HTTP 通訊設定、重試行為、服務區域端點設定，以及可能的任何 SDK 相關組態。使用此功能時，您可以取得針對常見使用案例量身打造的新組態預設值。

預設模式會跨所有 AWS SDKs標準化。適用於 Java 2.x 的 SDK 支援下列預設模式：
+ `legacy` – 提供預設設定，這些設定會因 AWS SDK 而異，而且在 `DefaultsMode` 建立之前已存在。
+ `standard` – 提供大多數案例的預設非最佳化設定。
+ `in-region` – 以標準模式為基礎，並包含為 AWS 服務 在相同 內呼叫的應用程式量身打造的設定 AWS 區域。
+ `cross-region` – 以標準模式為基礎，並為在不同區域中呼叫 AWS 服務 的應用程式提供具有高逾時值的設定。
+ `mobile` – 以標準模式為基礎，並為具有較高延遲的行動應用程式提供量身打造的高逾時設定。
+ `auto` – 以標準模式為基礎，並包含實驗性功能。SDK 會嘗試探索執行時期環境，自動判斷適當的設定。自動偵測是以啟發式為基礎，不提供 100% 的準確性。如果無法判斷執行時期環境，則會使用標準模式。自動偵測可能會查詢[執行個體中繼資料和使用者資料](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html)，這可能會導致延遲。如果啟動延遲對您的應用程式至關重要，我們建議您改為明確選擇 `DefaultsMode`。

您可以透過下列方式設定預設模式：
+ 直接在用戶端上，透過 `AwsClientBuilder.Builder#defaultsMode(DefaultsMode)`。
+ 在組態設定檔上，透過 `defaults_mode` 設定檔檔案屬性。
+ 全域，透過 `aws.defaultsMode` 系統屬性。
+ 全域，透過 `AWS_DEFAULTS_MODE` 環境變數。

**注意**  
對於 `legacy` 以外的任何模式，附加的預設值可能會隨著最佳實務的演進而變更。因此，如果您使用的是 `legacy` 以外的模式，建議您在升級 SDK 時執行測試。

*AWS SDK 和工具參考指南*中的[智慧組態預設](https://docs.aws.amazon.com/sdkref/latest/guide/feature-smart-config-defaults.html)會以不同的預設模式提供組態屬性及其預設值的清單。

您可以根據應用程式的特性和應用程式互動的 AWS 服務 來選擇預設模式值。

這些值的設定考量了 的廣泛選擇 AWS 服務 。對於 DynamoDB 資料表和應用程式都部署在一個區域中的典型 DynamoDB 部署，`in-region` 預設模式在 `standard` 預設模式之間最為相關。

**Example DynamoDB SDK 用戶端組態已針對低延遲呼叫進行調校**  
下列範例會將逾時調整為預期低延遲 DynamoDB 呼叫的較低值。  

```
DynamoDbAsyncClient asyncClient = DynamoDbAsyncClient.builder()
    .defaultsMode(DefaultsMode.IN_REGION)
    .httpClientBuilder(AwsCrtAsyncHttpClient.builder())
    .overrideConfiguration(ClientOverrideConfiguration.builder()
        .apiCallTimeout(Duration.ofSeconds(3))
        .apiCallAttemptTimeout(Duration.ofMillis(500))
        .build())
    .build();
```
個別 HTTP 用戶端實作可能會為您提供對逾時和連線使用行為的更精細控制。例如，對於以 AWS CRT 為基礎的用戶端，您可以啟用 `ConnectionHealthConfiguration`，讓用戶端主動監控已使用連線的運作狀態。如需詳細資訊，請參閱《 *AWS SDK for Java 2.x 開發人員指南*》中的 [AWS CRT 型 HTTP 用戶端的進階組態](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/http-configuration-crt.html#configuring-the-crt-based-http-client)。

### Keep-Alive 組態
<a name="KeepAliveConfig"></a>

啟用保持連線可以透過重複使用連線來減少延遲。有兩種不同類型的保持連線：HTTP Keep-Alive 和 TCP Keep-Alive。
+ HTTP Keep-Alive 會嘗試維護用戶端和伺服器之間的 HTTPS 連線，以便稍後的請求可以重複使用該連線。這會略過稍後請求的重型 HTTPS 身分驗證。HTTP Keep-Alive 預設為在所有用戶端上啟用。
+ TCP Keep-Alive 請求基礎作業系統透過通訊端連線傳送小型封包，以提供通訊端保持運作的額外保證，並立即偵測任何封包丟失。這可確保稍後的請求不會花時間嘗試使用被捨棄的通訊端。預設會針對所有用戶端停用 TCP Keep-Alive。下列程式碼範例示範如何在每個 HTTP 用戶端上啟用它。針對所有非 CRT 型 HTTP 用戶端啟用時，實際的 Keep-Alive 機制取決於作業系統。因此，您必須透過作業系統設定額外的 TCP Keep-Alive 值，例如逾時和封包數量。您可以在 Linux 或 macOS 上使用 `sysctl`，或在 Windows 上使用登錄值來執行此操作。

**Example 在 Apache 型 HTTP 用戶端上啟用 TCP Keep-Alive**  

```
DynamoDbClient client = DynamoDbClient.builder()
    .httpClientBuilder(ApacheHttpClient.builder().tcpKeepAlive(true))
    .build();
```

**`URLConnection` 型 HTTP 用戶端**  
任何使用 `URLConnection` 型 HTTP 用戶端的同步用戶端 [https://docs.oracle.com/javase/8/docs/api/java/net/HttpURLConnection.html](https://docs.oracle.com/javase/8/docs/api/java/net/HttpURLConnection.html) 都沒有啟用保持連線的[機制](https://docs.oracle.com/javase/8/docs/api/java/net/doc-files/net-properties.html)。

**Example 在 Netty 型 HTTP 用戶端上啟用 TCP Keep-Alive**  

```
DynamoDbAsyncClient client = DynamoDbAsyncClient.builder()
    .httpClientBuilder(NettyNioAsyncHttpClient.builder().tcpKeepAlive(true))
    .build();
```

**Example 在 AWS CRT 型 HTTP 用戶端上啟用 TCP Keep-Alive**  
使用 AWS CRT 型 HTTP 用戶端，您可以啟用 TCP 保持連線並控制持續時間。  

```
DynamoDbClient client = DynamoDbClient.builder()
    .httpClientBuilder(AwsCrtHttpClient.builder()
    .tcpKeepAliveConfiguration(TcpKeepAliveConfiguration.builder()
        .keepAliveInterval(Duration.ofSeconds(50))
        .keepAliveTimeout(Duration.ofSeconds(5))
        .build()))
    .build();
```
使用非同步 DynamoDB 用戶端時，您可以啟用 TCP Keep-Alive，如下列程式碼所示。  

```
DynamoDbAsyncClient client = DynamoDbAsyncClient.builder()
    .httpClientBuilder(AwsCrtAsyncHttpClient.builder()
    .tcpKeepAliveConfiguration(TcpKeepAliveConfiguration.builder()
        .keepAliveInterval(Duration.ofSeconds(50))
        .keepAliveTimeout(Duration.ofSeconds(5))
        .build()))
    .build();
```

## 錯誤處理
<a name="JavaErrorHandling"></a>

處理例外狀況時， AWS SDK for Java 2.x 會使用執行時間 （未勾選） 例外狀況。

涵蓋所有 SDK 例外狀況的基本例外狀況是 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/exception/SdkServiceException.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/exception/SdkServiceException.html)，其延伸自 Java 未檢查的 `RuntimeException`。如果您發現這種情況，您將會擷取到 SDK 擲回的所有例外狀況。

`SdkServiceException` 具有名為 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/awscore/exception/AwsServiceException.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/awscore/exception/AwsServiceException.html) 的子類別。此子類別表示與 AWS 服務通訊時的任何問題。它有一個名為 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/DynamoDbException.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/DynamoDbException.html) 的子類別，表示與 DynamoDB 通訊時發生問題。如果您發現這種情況，您將會擷取到與 DynamoDB 相關的所有例外狀況，但沒有其他 SDK 例外狀況。

在 `DynamoDbException` 下有更具體的[例外狀況類型](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/DynamoDbException.html)。其中一些例外狀況類型適用於控制平面操作，例如 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/TableAlreadyExistsException.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/TableAlreadyExistsException.html)。其他則適用於資料平面操作。以下是常見的資料平面例外狀況範例：
+ [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ConditionalCheckFailedException.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ConditionalCheckFailedException.html)：您已指定評估為 false 的條件。例如，您可能已嘗試對項目執行條件式更新，但屬性的實際值不符合條件中的預期值。以此方式失敗的請求不會重試。

其他情況則未定義特定的例外狀況。例如，當您的請求受到限流時，可能會擲回特定 `ProvisionedThroughputExceededException`，而在其他情況下則擲回更通用的 `DynamoDbException`。在任何一種情況下，您都可以檢查 `isThrottlingException()` 是否傳回 `true`，以判斷限流是否造成例外狀況。

根據您的應用程式需求，您可以擷取所有 `AwsServiceException` 或 `DynamoDbException` 執行個體。不過，在不同的情況下，您通常需要不同的行為。處理條件檢查失敗的邏輯與處理限流的邏輯不同。定義您要處理的例外路徑，並務必測試替代路徑。這可協助您確保可以處理所有相關案例。

如需可能遇到的常見錯誤清單，請參閱 [使用 DynamoDB 時發生錯誤](Programming.Errors.md)。另請參閱《*Amazon DynamoDB API 參考*》中的[常見錯誤](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/CommonErrors.html)。API 參考也為每個 API 操作 (例如 [https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html) 操作) 提供可能的確切錯誤。如需處理例外狀況的資訊，請參閱《*AWS SDK for Java 2.x 開發人員指南*》中的 [AWS SDK for Java 2.x例外狀況處理](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/handling-exceptions.html)。

## AWS 請求 ID
<a name="JavaRequestID"></a>

每個請求都包含一個請求 ID，如果您使用 AWS 支援 來診斷問題，這對於提取該 ID 很有幫助。衍生自 `SdkServiceException` 的每個例外狀況都有可用來擷取請求 ID 的 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/exception/SdkServiceException.html#requestId()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/exception/SdkServiceException.html#requestId()) 方法。

## 日誌
<a name="JavaLogging"></a>

使用 SDK 提供的日誌記錄，對於從用戶端程式庫擷取任何重要訊息以及更深入的偵錯目的都很有用。記錄器是階層式的，SDK 使用 `software.amazon.awssdk` 做為其根記錄器。您可以使用 `TRACE`、`DEBUG`、`INFO`、`WARN`、`ERROR`、`ALL` 或 `OFF` 其中之一來設定層級。設定的層級會套用至該記錄器，並沿著記錄器階層向下套用。

對於其記錄， AWS SDK for Java 2.x 使用適用於 Java 的 Simple Logging Façade (SLF4J)。這可做為其他記錄器周圍的抽象層，您可以使用它來插入您偏好的記錄器。如需有關插入記錄器的說明，請參閱 [SLF4J 使用者手冊](https://www.slf4j.org/manual.html)。

每個記錄器都有特定的行為。根據預設，Log4j 2.x 記錄器會建立 `ConsoleAppender`，其會將日誌事件附加至 `System.out`，並預設為 `ERROR` 日誌層級。

SLF4J 輸出中包含的 SimpleLogger 記錄器預設為 `System.err`，並預設為 `INFO` 日誌層級。

對於任何生產部署，建議您將 `WARN` 的階層設定為 `software.amazon.awssdk`，以便從 SDK 的用戶端程式庫擷取任何重要訊息，同時限制輸出數量。

如果 SLF4J 在類別路徑上找不到支援的記錄器 (無 SLF4J 繫結)，則會預設為[無操作實作](https://www.slf4j.org/codes.html#noProviders)。此實作會導致將訊息記錄於 `System.err`，說明 SLF4J 在 classpath 上找不到記錄器實作。若要避免這種情況，您必須新增記錄器實作。若要這樣做，您可以在 Apache Maven `pom.xml` 中新增對成品的相依性，例如 `org.slf4j.slf4j-simple` 或 `org.apache.logging.log4j.log4j-slf4j2-imp`。

如需有關如何在 SDK 中設定記錄的資訊，包括將記錄相依性新增至應用程式組態，請參閱《*適用於 Java 的 AWS SDK 開發人員指南*》中的[使用適用於 Java 2.x 的 SDK 記錄](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/logging-slf4j.html)。

如果您使用 Apache Log4j 2 記錄器，`Log4j2.xml` 檔案中的下列組態顯示如何調整記錄行為。此組態會將根記錄器層級設定為 `WARN`。階層中的所有記錄器都會繼承此記錄層級，包括 `software.amazon.awssdk` 記錄器。

根據預設，輸出會移至 `System.out`。在下列範例中，我們仍會覆寫預設輸出 Log4j 附加元件，以套用量身打造的 Log4j `PatternLayout`。

**`Log4j2.xml` 組態檔案的 範例**  
下列組態會在所有記錄器階層的 `ERROR` 和 `WARN` 層級將訊息記錄到主控台。

```
<Configuration status="WARN">
  <Appenders>
    <Console name="ConsoleAppender" target="SYSTEM_OUT">
      <PatternLayout pattern="%d{YYYY-MM-dd HH:mm:ss} [%t] %-5p %c:%L - %m%n" />
    </Console>
  </Appenders>

  <Loggers>
    <Root level="WARN">
      <AppenderRef ref="ConsoleAppender"/>
    </Root>
  </Loggers>
</Configuration>
```

### AWS 請求 ID 記錄
<a name="JavaReqIDLogging"></a>

當發生問題時，您可以在例外狀況中找到請求 ID。不過，如果您想要未產生例外狀況的請求 ID，則可以使用記錄功能。

`software.amazon.awssdk.request` 記錄器會在 `DEBUG` 層級輸出請求 ID。下列範例延伸先前的 [configuration example](#Log4j2ConfigEg)，將根記錄器層級保持在 `ERROR`、將 `software.amazon.awssdk` 保持在層級 `WARN`，以及將 `software.amazon.awssdk.request` 保持在層級 `DEBUG`。設定這些層級有助於擷取請求 ID 和其他請求相關詳細資訊，例如端點和狀態碼。

```
<Configuration status="WARN">
  <Appenders>
    <Console name="ConsoleAppender" target="SYSTEM_OUT">
      <PatternLayout pattern="%d{YYYY-MM-dd HH:mm:ss} [%t] %-5p %c:%L - %m%n" />
    </Console>
  </Appenders>

  <Loggers>
    <Root level="ERROR">
      <AppenderRef ref="ConsoleAppender"/>
    </Root>
    <Logger name="software.amazon.awssdk" level="WARN" />
    <Logger name="software.amazon.awssdk.request" level="DEBUG" />
  </Loggers>
</Configuration>
```

以下為日誌輸出的範例：

```
2022-09-23 16:02:08 [main] DEBUG software.amazon.awssdk.request:85 - Sending Request: DefaultSdkHttpFullRequest(httpMethod=POST, protocol=https, host=dynamodb.us-east-1.amazonaws.com, encodedPath=/, headers=[amz-sdk-invocation-id, Content-Length, Content-Type, User-Agent, X-Amz-Target], queryParameters=[])
 2022-09-23 16:02:08 [main] DEBUG software.amazon.awssdk.request:85 - Received successful response: 200, Request ID: QS9DUMME2NHEDH8TGT9N5V53OJVV4KQNSO5AEMVJF66Q9ASUAAJG, Extended Request ID: not available
```

## 分頁
<a name="JavaPagination"></a>

有些請求，例如 [https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html) 和 [https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Scan.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Scan.html)，會限制單一請求傳回的資料大小，並要求您重複提出請求以提取後續頁面。

您可以使用 `Limit` 參數控制每個頁面要讀取的項目數量上限。例如，您可以使用 `Limit` 參數僅擷取最後 10 個項目。此限制指定套用任何篩選之前要從資料表讀取的項目數量。如果您在篩選後只想要 10 個項目，則無法指定該項目數。您只能控制預先篩選的計數，並在實際擷取 10 個項目時在用戶端檢查。無論限制為何，回應的大小上限一律為 1 MB。

API 回應中可能包含 `LastEvaluatedKey`。這表示回應因為達到計數限制或大小限制而結束。此金鑰是針對該回應評估的最後一個金鑰。透過直接與 API 互動，您可以擷取此 `LastEvaluatedKey`，並將其傳遞至後續呼叫做為 `ExclusiveStartKey`，以便從該起點讀取下一個區塊。如果沒有傳回任何 `LastEvaluatedKey`，表示沒有更多符合 `Query` 或 `Scan` API 呼叫的項目。

下列範例使用低階介面，根據 `keyConditionExpression` 參數將項目限制為 100。

```
QueryRequest.Builder queryRequestBuilder = QueryRequest.builder()
        .expressionAttributeValues(Map.of(
                ":pk_val", AttributeValue.fromS("123"),
                ":sk_val", AttributeValue.fromN("1000")))
        .keyConditionExpression("pk = :pk_val AND sk > :sk_val")
        .limit(100)
        .tableName(TABLE_NAME);

while (true) {
    QueryResponse queryResponse = DYNAMODB_CLIENT.query(queryRequestBuilder.build());

    queryResponse.items().forEach(item -> {
        LOGGER.info("item PK: [" + item.get("pk") + "] and SK: [" + item.get("sk") + "]");
    });

    if (!queryResponse.hasLastEvaluatedKey()) {
        break;
    }
    queryRequestBuilder.exclusiveStartKey(queryResponse.lastEvaluatedKey());
}
```

 AWS SDK for Java 2.x 可以透過提供自動分頁方法，進行多次服務呼叫，以自動為您取得後續頁面的結果，簡化與 DynamoDB 的互動。這樣可以簡化您的程式碼，但它也移除了手動讀取頁面時所能保持對資源用量的一些控制。

透過使用 DynamoDB 用戶端中可用的 `Iterable` 方法，例如 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html#queryPaginator(software.amazon.awssdk.services.dynamodb.model.QueryRequest)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html#queryPaginator(software.amazon.awssdk.services.dynamodb.model.QueryRequest)) 和 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html#scanPaginator(software.amazon.awssdk.services.dynamodb.model.ScanRequest)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html#scanPaginator(software.amazon.awssdk.services.dynamodb.model.ScanRequest))，SDK 會負責處理分頁。這些方法的傳回類型是自訂可迭代物件，可用來逐一查看所有頁面。SDK 會在內部為您處理服務呼叫。您可以使用 Java 串流 API 處理 `QueryPaginator` 的結果，如下列範例所示。

```
QueryPublisher queryPublisher =
    DYNAMODB_CLIENT.queryPaginator(QueryRequest.builder()
        .expressionAttributeValues(Map.of(
            ":pk_val", AttributeValue.fromS("123"),
            ":sk_val", AttributeValue.fromN("1000")))
        .keyConditionExpression("pk = :pk_val AND sk > :sk_val")
        .limit(100)
        .tableName("YourTableName")
        .build());

queryPublisher.items().subscribe(item ->
    System.out.println(item.get("itemData"))).join();
```

## 資料類別註釋
<a name="JavaDataClassAnnotation"></a>

Java SDK 提供數個註釋，您可以將這些註釋放在資料類別的屬性上。這些註釋會影響 SDK 如何與屬性互動。透過新增註釋，您可以讓屬性做為隱含原子計數器、維護自動產生的時間戳記值，或追蹤項目版本號碼。如需詳細資訊，請參閱[資料類別註釋](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/ddb-en-client-anno-index.html)。