

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

# AWS 服務 從 呼叫 AWS SDK for Java 2.x
<a name="work-with-services"></a>

本節提供如何使用 Select 的簡短教學課程和指導 AWS 服務。如需完整的範例集，請參閱[程式碼範例一節](java_code_examples.md)。

**Topics**
+ [CloudWatch](examples-cloudwatch.md)
+ [AWS 資料庫服務](examples-databases.md)
+ [DynamoDB](examples-dynamodb.md)
+ [Amazon EC2](examples-ec2.md)
+ [IAM](examples-iam.md)
+ [Kinesis](examples-kinesis.md)
+ [Lambda](examples-lambda.md)
+ [Amazon S3](examples-s3.md)
+ [Amazon SNS](examples-simple-notification-service.md)
+ [Amazon SQS](examples-sqs.md)
+ [Amazon Transcribe](examples-transcribe.md)

# 使用 CloudWatch
<a name="examples-cloudwatch"></a>

本節提供使用 適用於 Java 的 AWS SDK 2.x 編寫 [Amazon CloudWatch](https://docs.aws.amazon.com//AmazonCloudWatch/latest/monitoring/WhatIsCloudWatch.html) 程式的範例。

 Amazon CloudWatch AWS 會即時監控您的 Amazon Web Services (AWS) 資源和您在 上執行的應用程式。您可以使用 CloudWatch 收集和追蹤指標，這些是您可以針對資源和應用程式測量的變數。 CloudWatch alarms 會根據您定義的規則傳送通知或自動變更您正在監控的資源。

下列範例僅包含示範每個技術所需的程式碼。[GitHub 上提供完整程式碼範例](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2)。您可以從那裡下載單一原始檔案或將儲存庫複製到本機，以取得建置和執行的所有範例。

**Topics**
+ [從 取得指標 CloudWatch](examples-cloudwatch-get-metrics.md)
+ [將自訂指標資料發佈至 CloudWatch](examples-cloudwatch-publish-custom-metrics.md)
+ [使用 CloudWatch 警示](examples-cloudwatch-create-alarms.md)
+ [使用 Amazon CloudWatch Events](examples-cloudwatch-send-events.md)

# 從 取得指標 CloudWatch
<a name="examples-cloudwatch-get-metrics"></a>

## 列出指標
<a name="listing-metrics"></a>

若要列出 CloudWatch 指標，請建立 [ListMetricsRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/cloudwatch/model/ListMetricsRequest.html) 並呼叫 CloudWatchClient 的 `listMetrics`方法。您可以使用 `ListMetricsRequest` 來根據命名空間、指標名稱或維度，篩選傳回的指標。

**注意**  
您可以在 Amazon CloudWatch 《 使用者指南》的指標和維度參考中找到 AWS 服務發佈的指標和維度清單。 [Amazon CloudWatch](https://docs.aws.amazon.com//AmazonCloudWatch/latest/monitoring/aws-services-cloudwatch-metrics.html)

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.cloudwatch.CloudWatchClient;
import software.amazon.awssdk.services.cloudwatch.model.CloudWatchException;
import software.amazon.awssdk.services.cloudwatch.model.ListMetricsRequest;
import software.amazon.awssdk.services.cloudwatch.model.ListMetricsResponse;
import software.amazon.awssdk.services.cloudwatch.model.Metric;
```

 **Code** 

```
    public static void listMets( CloudWatchClient cw, String namespace) {

        boolean done = false;
        String nextToken = null;

        try {
            while(!done) {

                ListMetricsResponse response;

                if (nextToken == null) {
                   ListMetricsRequest request = ListMetricsRequest.builder()
                        .namespace(namespace)
                        .build();

                 response = cw.listMetrics(request);
                } else {
                  ListMetricsRequest request = ListMetricsRequest.builder()
                        .namespace(namespace)
                        .nextToken(nextToken)
                        .build();

                response = cw.listMetrics(request);
            }

            for (Metric metric : response.metrics()) {
                System.out.printf(
                        "Retrieved metric %s", metric.metricName());
                System.out.println();
            }

            if(response.nextToken() == null) {
                done = true;
            } else {
                nextToken = response.nextToken();
            }
        }

        } catch (CloudWatchException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

透過呼叫其 [ 方法，指標會在 ](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/cloudwatch/model/ListMetricsResponse.html)ListMetricsResponse`getMetrics` 中傳回。

結果可能會*分頁*。若要擷取下一批次結果，請在回應物件上呼叫 `nextToken`，並使用字符值來建置新的請求物件。然後以新的請求再次呼叫 `listMetrics` 方法。

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f4eaf2b2971805cfb2b87a8e5ab408f83169432e/javav2/example_code/cloudwatch/src/main/java/com/example/cloudwatch/ListMetrics.java)。

## 其他資訊
<a name="more-information"></a>
+  Amazon CloudWatch API 參考中的 [ListMetrics](https://docs.aws.amazon.com//AmazonCloudWatch/latest/APIReference/API_ListMetrics.html) 

# 將自訂指標資料發佈至 CloudWatch
<a name="examples-cloudwatch-publish-custom-metrics"></a>

許多 AWS 服務會在以「`AWS`」開頭的命名空間中發佈[自己的指標](https://docs.aws.amazon.com//AmazonCloudWatch/latest/monitoring/aws-services-cloudwatch-metrics.html)。您也可以使用自己的命名空間 （只要不是以「`AWS`」開頭） 來發佈自訂指標資料。

## 發佈自訂指標資料
<a name="cwid1"></a>

若要發佈您自己的指標資料，請使用 [PutMetricDataRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/cloudwatch/model/PutMetricDataRequest.html) 呼叫 CloudWatchClient 的 `putMetricData`方法。`PutMetricDataRequest` 必須在 [MetricDatum](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/cloudwatch/model/MetricDatum.html) 物件中包含用於資料的自訂命名空間，以及資料點本身的相關資訊。

**注意**  
您無法指定開頭為 " `AWS` " 的命名空間。以「`AWS`」開頭的命名空間保留供 Amazon Web Services 產品使用。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.cloudwatch.CloudWatchClient;
import software.amazon.awssdk.services.cloudwatch.model.Dimension;
import software.amazon.awssdk.services.cloudwatch.model.MetricDatum;
import software.amazon.awssdk.services.cloudwatch.model.StandardUnit;
import software.amazon.awssdk.services.cloudwatch.model.PutMetricDataRequest;
import software.amazon.awssdk.services.cloudwatch.model.CloudWatchException;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
```

 **Code** 

```
    public static void putMetData(CloudWatchClient cw, Double dataPoint ) {

        try {
            Dimension dimension = Dimension.builder()
                    .name("UNIQUE_PAGES")
                    .value("URLS")
                    .build();

            // Set an Instant object
            String time = ZonedDateTime.now( ZoneOffset.UTC ).format( DateTimeFormatter.ISO_INSTANT );
            Instant instant = Instant.parse(time);

            MetricDatum datum = MetricDatum.builder()
                .metricName("PAGES_VISITED")
                .unit(StandardUnit.NONE)
                .value(dataPoint)
                .timestamp(instant)
                .dimensions(dimension).build();

            PutMetricDataRequest request = PutMetricDataRequest.builder()
                .namespace("SITE/TRAFFIC")
                .metricData(datum).build();

            cw.putMetricData(request);

        } catch (CloudWatchException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
        System.out.printf("Successfully put data point %f", dataPoint);
     }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f4eaf2b2971805cfb2b87a8e5ab408f83169432e/javav2/example_code/cloudwatch/src/main/java/com/example/cloudwatch/PutMetricData.java)。

## 其他資訊
<a name="more-information"></a>
+  Amazon CloudWatch 《 使用者指南》中的[使用 Amazon CloudWatch 指標](https://docs.aws.amazon.com//AmazonCloudWatch/latest/monitoring/working_with_metrics.html)。
+  Amazon CloudWatch 《 使用者指南[AWS 》中的命名空間](https://docs.aws.amazon.com//AmazonCloudWatch/latest/monitoring/aws-services-cloudwatch-metrics.html)。
+  Amazon CloudWatch API 參考中的 [PutMetricData](https://docs.aws.amazon.com//AmazonCloudWatch/latest/APIReference/API_PutMetricData.html)。

# 使用 CloudWatch 警示
<a name="examples-cloudwatch-create-alarms"></a>

## 建立警示
<a name="create-an-alarm"></a>

若要根據 CloudWatch 指標建立警示，請呼叫 CloudWatchClient 的 `putMetricAlarm`方法，並將 [PutMetricAlarmRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/cloudwatch/model/PutMetricAlarmRequest.html) 填入警示條件。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.cloudwatch.CloudWatchClient;
import software.amazon.awssdk.services.cloudwatch.model.Dimension;
import software.amazon.awssdk.services.cloudwatch.model.PutMetricAlarmRequest;
import software.amazon.awssdk.services.cloudwatch.model.ComparisonOperator;
import software.amazon.awssdk.services.cloudwatch.model.Statistic;
import software.amazon.awssdk.services.cloudwatch.model.StandardUnit;
import software.amazon.awssdk.services.cloudwatch.model.CloudWatchException;
```

 **Code** 

```
    public static void putMetricAlarm(CloudWatchClient cw, String alarmName, String instanceId) {

        try {
            Dimension dimension = Dimension.builder()
                .name("InstanceId")
                .value(instanceId).build();

            PutMetricAlarmRequest request = PutMetricAlarmRequest.builder()
                .alarmName(alarmName)
                .comparisonOperator(
                        ComparisonOperator.GREATER_THAN_THRESHOLD)
                .evaluationPeriods(1)
                .metricName("CPUUtilization")
                .namespace("AWS/EC2")
                .period(60)
                .statistic(Statistic.AVERAGE)
                .threshold(70.0)
                .actionsEnabled(false)
                .alarmDescription(
                        "Alarm when server CPU utilization exceeds 70%")
                .unit(StandardUnit.SECONDS)
                .dimensions(dimension)
                .build();

            cw.putMetricAlarm(request);
            System.out.printf(
                    "Successfully created alarm with name %s", alarmName);

        } catch (CloudWatchException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f4eaf2b2971805cfb2b87a8e5ab408f83169432e/javav2/example_code/cloudwatch/src/main/java/com/example/cloudwatch/PutMetricAlarm.java)。

## 列出警示
<a name="list-alarms"></a>

若要列出您已建立的 CloudWatch 警示，請使用 [DescribeAlarmsRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/cloudwatch/model/DescribeAlarmsRequest.html) 呼叫 CloudWatchClient 的 `describeAlarms`方法，以用於設定結果的選項。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.cloudwatch.CloudWatchClient;
import software.amazon.awssdk.services.cloudwatch.model.CloudWatchException;
import software.amazon.awssdk.services.cloudwatch.model.DescribeAlarmsRequest;
import software.amazon.awssdk.services.cloudwatch.model.DescribeAlarmsResponse;
import software.amazon.awssdk.services.cloudwatch.model.MetricAlarm;
```

 **Code** 

```
    public static void desCWAlarms( CloudWatchClient cw) {

        try {

            boolean done = false;
            String newToken = null;

            while(!done) {
                DescribeAlarmsResponse response;

                if (newToken == null) {
                    DescribeAlarmsRequest request = DescribeAlarmsRequest.builder().build();
                    response = cw.describeAlarms(request);
                } else {
                    DescribeAlarmsRequest request = DescribeAlarmsRequest.builder()
                        .nextToken(newToken)
                        .build();
                    response = cw.describeAlarms(request);
                }

                for(MetricAlarm alarm : response.metricAlarms()) {
                    System.out.printf("\n Retrieved alarm %s", alarm.alarmName());
                }

                if(response.nextToken() == null) {
                    done = true;
                } else {
                    newToken = response.nextToken();
                }
            }

        } catch (CloudWatchException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
        System.out.printf("Done");
    }
```

您可以透過在 `describeAlarms` 傳回的 [DescribeAlarmsResponse](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/cloudwatch/model/DescribeAlarmsResponse.html) 上呼叫 `MetricAlarms`，來取得警示清單。

結果可能會*分頁*。若要擷取下一批次結果，請在回應物件上呼叫 `nextToken`，並使用字符值來建置新的請求物件。然後以新的請求再次呼叫 `describeAlarms` 方法。

**注意**  
您也可以使用 CloudWatchClient 的 `describeAlarmsForMetric`方法擷取特定指標的警示。其用法類似於 `describeAlarms`。

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f4eaf2b2971805cfb2b87a8e5ab408f83169432e/javav2/example_code/cloudwatch/src/main/java/com/example/cloudwatch/DescribeAlarms.java)。

## 刪除警示
<a name="delete-alarms"></a>

若要刪除 CloudWatch 警示，請使用 [DeleteAlarmsRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/cloudwatch/model/DeleteAlarmsRequest.html) 呼叫 CloudWatchClient 的 `deleteAlarms`方法，其中包含一或多個您要刪除的警示名稱。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.cloudwatch.CloudWatchClient;
import software.amazon.awssdk.services.cloudwatch.model.CloudWatchException;
import software.amazon.awssdk.services.cloudwatch.model.DeleteAlarmsRequest;
```

 **Code** 

```
    public static void deleteCWAlarm(CloudWatchClient cw, String alarmName) {

        try {
            DeleteAlarmsRequest request = DeleteAlarmsRequest.builder()
                    .alarmNames(alarmName)
                    .build();

            cw.deleteAlarms(request);
            System.out.printf("Successfully deleted alarm %s", alarmName);

        } catch (CloudWatchException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f4eaf2b2971805cfb2b87a8e5ab408f83169432e/javav2/example_code/cloudwatch/src/main/java/com/example/cloudwatch/DeleteAlarm.java)。

## 其他資訊
<a name="more-information"></a>
+  Amazon CloudWatch 《 使用者指南》中的[使用 Amazon CloudWatch 警示](https://docs.aws.amazon.com//AmazonCloudWatch/latest/monitoring/AlarmThatSendsEmail.html) 
+  Amazon CloudWatch API 參考中的 [PutMetricAlarm](https://docs.aws.amazon.com//AmazonCloudWatch/latest/APIReference/API_PutMetricAlarm.html) 
+  Amazon CloudWatch API 參考中的 [DescribeAlarms](https://docs.aws.amazon.com//AmazonCloudWatch/latest/APIReference/API_DescribeAlarms.html) 
+  Amazon CloudWatch API 參考中的 [DeleteAlarms](https://docs.aws.amazon.com//AmazonCloudWatch/latest/APIReference/API_DeleteAlarms.html) 

# 使用 Amazon CloudWatch Events
<a name="examples-cloudwatch-send-events"></a>

 CloudWatch Events 提供近乎即時的系統事件串流，描述 Amazon EC2 執行個體、 Lambda 函數、 Kinesis 串流、 Amazon ECS 任務、 Step Functions 狀態機器、 Amazon SNS 主題、 Amazon SQS 佇列或內建目標 AWS 的資源變更。您可以使用簡單的規則，來比對事件，並將這些事件轉傳到一或多個目標函數或串流。

Amazon EventBridge 是 CloudWatch Events 的[演變](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-cwe-now-eb.html)。這兩個服務都使用相同的 API，因此您可以繼續使用 SDK 提供的 [CloudWatch Events 用戶端](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/cloudwatch/CloudWatchClient.html)，或遷移至適用於 Java 的 SDK [EventBridge 用戶端](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/eventbridge/EventBridgeClient.html)以取得 CloudWatch Events 功能。CloudWatch Events [使用者指南文件](https://docs.aws.amazon.com/eventbridge/latest/userguide/index.html)和 [API 參考](https://docs.aws.amazon.com/eventbridge/latest/APIReference/index.html)現在可透過 EventBridge 文件網站取得。

## 新增事件
<a name="add-events"></a>

若要新增自訂 CloudWatch 事件，請使用包含一或多個[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/cloudwatchevents/model/PutEventsRequestEntry.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/cloudwatchevents/model/PutEventsRequestEntry.html)物件的[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/cloudwatchevents/model/PutEventsRequest.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/cloudwatchevents/model/PutEventsRequest.html)物件呼叫 `CloudWatchEventsClient’s``putEvents`方法，以提供每個事件的詳細資訊。您可以指定項目的多個參數，例如事件的來源和類型、與事件相關聯的資源等等。

**注意**  
對 `putEvents` 的每個呼叫最多可以指定 10 個事件。

 **匯入** 

```
import software.amazon.awssdk.services.cloudwatch.model.CloudWatchException;
import software.amazon.awssdk.services.cloudwatchevents.CloudWatchEventsClient;
import software.amazon.awssdk.services.cloudwatchevents.model.PutEventsRequest;
import software.amazon.awssdk.services.cloudwatchevents.model.PutEventsRequestEntry;
```

 **Code** 

```
    public static void putCWEvents(CloudWatchEventsClient cwe, String resourceArn ) {

        try {

            final String EVENT_DETAILS =
                "{ \"key1\": \"value1\", \"key2\": \"value2\" }";

            PutEventsRequestEntry requestEntry = PutEventsRequestEntry.builder()
                    .detail(EVENT_DETAILS)
                    .detailType("sampleSubmitted")
                    .resources(resourceArn)
                    .source("aws-sdk-java-cloudwatch-example")
                    .build();

            PutEventsRequest request = PutEventsRequest.builder()
                    .entries(requestEntry)
                    .build();

            cwe.putEvents(request);
            System.out.println("Successfully put CloudWatch event");

        } catch (CloudWatchException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/cloudwatch/src/main/java/com/example/cloudwatch/PutEvents.java)。

## 新增規則
<a name="add-rules"></a>

若要建立或更新規則，請使用規則[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/cloudwatchevents/model/PutRuleRequest.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/cloudwatchevents/model/PutRuleRequest.html)名稱的 和選用參數呼叫 `CloudWatchEventsClient’s``putRule`方法，例如[事件模式](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-event-patterns.html)、與規則建立關聯的 IAM 角色，以及描述規則執行頻率的[排程表達](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-create-rule-schedule.html)式。

 **匯入** 

```
import software.amazon.awssdk.services.cloudwatch.model.CloudWatchException;
import software.amazon.awssdk.services.cloudwatchevents.CloudWatchEventsClient;
import software.amazon.awssdk.services.cloudwatchevents.model.PutRuleRequest;
import software.amazon.awssdk.services.cloudwatchevents.model.PutRuleResponse;
import software.amazon.awssdk.services.cloudwatchevents.model.RuleState;
```

 **Code** 

```
    public static void putCWRule(CloudWatchEventsClient cwe, String ruleName, String roleArn) {

        try {
            PutRuleRequest request = PutRuleRequest.builder()
                .name(ruleName)
                .roleArn(roleArn)
                .scheduleExpression("rate(5 minutes)")
                .state(RuleState.ENABLED)
                .build();

            PutRuleResponse response = cwe.putRule(request);
            System.out.printf(
                    "Successfully created CloudWatch events rule %s with arn %s",
                    roleArn, response.ruleArn());
        } catch (
            CloudWatchException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/cloudwatch/src/main/java/com/example/cloudwatch/PutRule.java)。

## 新增目標
<a name="add-targets"></a>

目標是觸發規則時叫用的資源。範例目標包括 Amazon EC2 執行個體、 Lambda 函數、 Kinesis 串流、 Amazon ECS 任務、 Step Functions 狀態機器和內建目標。

若要將目標新增至規則，請呼叫 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/cloudwatchevents/model/PutTargetsRequest.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/cloudwatchevents/model/PutTargetsRequest.html) `CloudWatchEventsClient’s``putTargets`方法，其中包含要更新的規則和要新增至規則的目標清單。

 **匯入** 

```
import software.amazon.awssdk.services.cloudwatch.model.CloudWatchException;
import software.amazon.awssdk.services.cloudwatchevents.CloudWatchEventsClient;
import software.amazon.awssdk.services.cloudwatchevents.model.PutTargetsRequest;
import software.amazon.awssdk.services.cloudwatchevents.model.PutTargetsResponse;
import software.amazon.awssdk.services.cloudwatchevents.model.Target;
```

 **Code** 

```
    public static void putCWTargets(CloudWatchEventsClient cwe, String ruleName, String functionArn, String targetId ) {

        try {
            Target target = Target.builder()
                .arn(functionArn)
                .id(targetId)
                .build();

            PutTargetsRequest request = PutTargetsRequest.builder()
                .targets(target)
                .rule(ruleName)
                .build();

            PutTargetsResponse response = cwe.putTargets(request);
            System.out.printf(
                "Successfully created CloudWatch events target for rule %s",
                ruleName);
        } catch (CloudWatchException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/cloudwatch/src/main/java/com/example/cloudwatch/PutTargets.java)。

## 其他資訊
<a name="more-information"></a>
+  《Amazon EventBridge 使用者指南》中的[使用 PutEvents 新增](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-putevents.html)事件
+  《Amazon EventBridge 使用者指南》中的[規則排程表達式](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-create-rule-schedule.html#eb-create-scheduled-rule-schedule) 
+  《Amazon EventBridge 使用者指南》中的 [的事件類型 CloudWatch Events](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-service-event.html) 
+  《Amazon EventBridge 使用者指南》中的[事件模式](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-event-patterns.html) 
+  Amazon EventBridge API 參考中的 [PutEvents](https://docs.aws.amazon.com/eventbridge/latest/APIReference/API_PutEvents.html) 
+  Amazon EventBridge API 參考中的 [PutTargets](https://docs.aws.amazon.com/eventbridge/latest/APIReference/API_PutTargets.html) 
+  Amazon EventBridge API 參考中的 [PutRule](https://docs.aws.amazon.com/eventbridge/latest/APIReference/API_PutRule.html) 

# AWS 資料庫服務和 AWS SDK for Java 2.x
<a name="examples-databases"></a>

AWS 提供數種資料庫類型：關聯式、鍵值、記憶體內、文件[等](https://aws.amazon.com/products/databases/)。適用於 Java 的 SDK 2.x 支援會因資料庫服務的性質而有所不同。 AWS

有些資料庫服務，例如 [Amazon DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/Welcome.html) 服務，具有 Web APIs 來管理 AWS 資源 （資料庫），以及 Web 服務 APIs來與資料互動。在適用於 Java 的 SDK 2.x 中，這些類型的服務具有專用服務用戶端，例如 [DynamoDBClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html)。

其他資料庫服務具有與 資源互動的 Web 服務 APIs，例如 [Amazon DocumentDB](https://docs.aws.amazon.com/documentdb/latest/developerguide/api-reference.html) API （用於叢集、執行個體和資源管理），但沒有用於處理資料的 Web 服務 API。適用於 Java 的 SDK 2.x 具有對應的 [DocDbClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/docdb/DocDbClient.html) 介面，可用於 資源。不過，您需要另一個 Java API，例如適用於 [Java 的 MongoDB](https://www.mongodb.com/developer/languages/java/)，才能使用資料。

使用下列範例，了解如何使用適用於 Java 的 SDK 2.x 服務用戶端搭配不同類型的資料庫。

## Amazon DynamoDB 範例
<a name="examples-db-dynamodb"></a>


| 使用資料 | 使用資料庫 | 
| --- |--- |
| SDK service client: [DynamoDbClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html) | SDK service client: [DynamoDbClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html) | 
| Example: [使用 DynamoDB 的 React/Spring REST 應用程式](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2/usecases/creating_dynamodb_web_app) | Examples: [CreateTable、ListTables、DeleteTable](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2/example_code/dynamodb/src/main/java/com/example/dynamodb) | 
| Examples: [數個 DynamoDB 範例](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2/example_code/dynamodb/src/main/java/com/example/dynamodb) |  | 
|  | 
| --- |
| SDK service client: [DynamoDbEnhancedClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html) |  | 
| Example: [使用 DynamoDB 的 React/Spring REST 應用程式](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2/usecases/creating_dynamodb_web_app) |  | 
| Examples: [數個 DynamoDB 範例](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2/example_code/dynamodb/src/main/java/com/example/dynamodb) (names starting with 'Enhanced") |  | 

請參閱本指南引導式程式碼[範例一節中的其他 DynamoDB ](examples-dynamodb.md) 範例。

## Amazon RDS 範例
<a name="examples-db-rds"></a>


|  使用資料  |  使用資料庫  | 
| --- | --- | 
| 非 SDK API：JDBC，資料庫特定的 SQL 樣式；您的程式碼會管理資料庫連線或連線集區。 | SDK 服務用戶端：[RdsClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/rds/RdsClient.html) | 
| 範例：[使用 MySQL 的 React/Spring REST 應用程式](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2/usecases/Creating_rds_item_tracker) | 範例：[數個 RdsClient 範例](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2/example_code/rds/src/main/java/com/example/rds) | 

## Amazon Redshift 範例
<a name="examples-db-redshift"></a>


|  使用資料  |  使用資料庫  | 
| --- | --- | 
| SDK 服務用戶端：[RedshiftDataClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/redshiftdata/RedshiftDataClient.html) | SDK 服務用戶端：[RedshiftClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/redshift/RedshiftClient.html) | 
| 範例：[數個 RedshiftDataClient 範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/c682a07a1e6abce793e3c32ef3b9661fa723d0ff/javav2/example_code/redshift/src/main/java/com/example/scenario/RedshiftScenario.java) | 範例：[數個 RedshiftClient 範例](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2/example_code/redshift/src/main/java/com/example/redshift) | 
| 範例：[使用 RedshiftDataClient 的 React/Spring REST 應用程式](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2/usecases/CreatingSpringRedshiftRest) |  | 

## Amazon Aurora Serverless v2 範例
<a name="examples-db-aurora-sv1"></a>


|  使用資料  |  使用資料庫  | 
| --- | --- | 
| SDK 服務用戶端：[RdsDataClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/rdsdata/RdsDataClient.html) | SDK 服務用戶端：[RdsClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/rds/RdsClient.html) | 
| 範例：[使用 RdsDataClient 的 React/Spring REST 應用程式](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2/usecases/Creating_Spring_RDS_Rest) | 範例：[數個 RdsClient 範例](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2/example_code/rds/src/main/java/com/example/rds) | 

## Amazon DocumentDB 範例
<a name="examples-db-docdb"></a>


|  使用資料  |  使用資料庫  | 
| --- | --- | 
| 非 SDK API：MongoDB 特定的 Java 程式庫 （例如適用於 [Java 的 MongoDB](https://www.mongodb.com/developer/languages/java/))；您的程式碼會管理資料庫連線或連線集區。 | SDK 服務用戶端：[DocDbClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/docdb/DocDbClient.html) | 
| 範例：[DocumentDB (Mongo) 開發人員指南](https://docs.aws.amazon.com/documentdb/latest/developerguide/connect_programmatically.html#connect_programmatically-tls_enabled) （選取「Java」索引標籤） |  | 

# 使用 DynamoDB
<a name="examples-dynamodb"></a>

[DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Introduction.html) 是全受管的 NoSQL 資料庫服務，可提供快速且可預測的效能和無縫的可擴展性。本節說明如何使用 適用於 Java 的 AWS SDK 2.x 使用 DynamoDB。

## 選擇您的 DynamoDB 用戶端
<a name="ddb-clients-overview"></a>

開發套件提供兩種使用 DynamoDB 的主要方法：

低階客戶端 (`DynamoDbClient`)  
提供 DynamoDB 操作的直接存取權，並完全控制請求和回應。當您需要精細控制或使用動態結構描述時，請使用此用戶端。

增強型用戶端 (`DynamoDbEnhancedClient`)  
提供具有 Java 物件和 DynamoDB 項目之間自動映射的物件導向程式設計。也提供文件導向功能，用於處理不遵循固定結構描述的類似 JSON 的資料。使用定義明確的資料模型或文件類型資料時，請使用此用戶端。

## 設定 DynamoDB 用戶端
<a name="ddb-configuration-setup"></a>

使用 DynamoDB 之前，請設定您的用戶端以取得最佳效能和可靠性。

### 了解 DynamoDB 重試行為
<a name="ddb-retry-behavior"></a>

DynamoDB 用戶端使用預設的重試計數上限 8，高於其他 AWS 服務 用戶端。此較高的重試計數有助於處理 DynamoDB 的分散式本質和暫時容量限制。如需重試策略的詳細資訊，請參閱 [在 中設定重試行為 AWS SDK for Java 2.x](retry-strategy.md)。

### 使用帳戶型端點最佳化效能
<a name="ddb-account-based-endpoints-v2"></a>

DynamoDB 提供以[AWS 帳戶為基礎的端點](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Programming.SDKOverview.html#Programming.SDKs.endpoints)，透過使用 AWS 您的帳戶 ID 來簡化請求路由來改善效能。

若要使用此功能，您需要 2.28.4 版或更新版本的 AWS SDK for Java 2.x。您可以在 [Maven 中央儲存庫](https://central.sonatype.com/artifact/software.amazon.awssdk/bom/versions)中找到最新版本。支援的 SDK 版本會自動使用新的端點。

若要選擇退出以帳戶為基礎的路由，請選擇下列其中一個選項：
+ 將 DynamoDB 服務用戶端`AccountIdEndpointMode`設定為 `DISABLED`。
+ 設定環境變數。
+ 設定 JVM 系統屬性。
+ 更新共用 AWS 的組態檔案設定。

下列範例示範如何透過設定 DynamoDB 服務用戶端來停用帳戶型路由：

```
DynamoDbClient.builder()
                 .accountIdEndpointMode(AccountIdEndpointMode.DISABLED)
                 .build();
```

如需其他組態選項的詳細資訊，請參閱《 AWS SDKs和工具參考指南》中的[帳戶型端點](https://docs.aws.amazon.com/sdkref/latest/guide/feature-account-endpoints.html)。

## 本主題涵蓋的內容
<a name="ddb-topics-overview"></a>

下列各節說明如何使用 DynamoDB：
+ [在 中使用資料表 DynamoDB](examples-dynamodb-tables.md) - 建立、描述、更新和刪除資料表
+ [在 中使用項目 DynamoDB](examples-dynamodb-items.md) - 新增、擷取和更新個別項目
+ [使用 將 Java 物件映射至 DynamoDB 項目 AWS SDK for Java 2.x](dynamodb-enhanced-client.md) - 搭配增強型用戶端使用物件映射和文件導向資料

如需其他 DynamoDB 程式碼範例，請參閱[程式碼範例程式庫中的 DynamoDB ](java_dynamodb_code_examples.md) AWS 程式碼範例。

# 在 中使用資料表 DynamoDB
<a name="examples-dynamodb-tables"></a>

資料表是 DynamoDB 資料庫中所有項目的容器。您必須先建立資料表 DynamoDB，才能從中新增或移除資料。

對於每個資料表，您必須定義：
+ 您帳戶和區域唯一的資料表*名稱*。
+ 每個值的*主索引鍵*都必須獨一無二，資料表中任兩個項目不能有相同的主索引鍵值。

  主索引鍵可以是*簡單的*，包含單一分割區 (HASH) 索引鍵；也可以是*複合的*，包含分割區和排序 (RANGE) 索引鍵。

  每個索引鍵值都有一個相關聯的*資料類型*，由 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ScalarAttributeType.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ScalarAttributeType.html)類別列舉。索引鍵值可以是二進位 (B)、數值 (N)、或字串 (S)。如需詳細資訊，請參閱《 Amazon DynamoDB 開發人員指南》中的[命名規則和資料類型](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html)。
+  *佈建輸送量*值用於定義為資料表預留的讀取/寫入容量單位數。
**注意**  
 [Amazon DynamoDB 定價](https://aws.amazon.com/dynamodb/pricing/)是以您在資料表上設定的佈建輸送量值為基礎，因此請只預留您認為資料表所需的容量。

  資料表的佈建輸送量隨時可以修改，所以您可以在需求變更時調整容量。

## 建立資料表
<a name="dynamodb-create-table"></a>

使用 `DynamoDbClient’s``createTable`方法來建立新的 DynamoDB 資料表。您需要建構資料表屬性和資料表結構描述，這兩項都會用來識別資料表的主索引鍵。您也必須提供初始佈建的輸送量值和資料表名稱。

**注意**  
如果具有您所選名稱的資料表已存在，`[DynamoDbException](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/DynamoDbException.html)`則會擲回 。

### 使用簡單主索引鍵建立資料表
<a name="dynamodb-create-table-simple"></a>

此程式碼會建立一個具有一個屬性的資料表，該屬性是資料表的簡單主索引鍵。 範例使用 `[AttributeDefinition](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/AttributeDefinition.html)`和 `[KeySchemaElement](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/KeySchemaElement.html)` 物件做為 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/CreateTableRequest.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/CreateTableRequest.html)。

 **匯入** 

```
import software.amazon.awssdk.core.waiters.WaiterResponse;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest;
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement;
import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput;
import software.amazon.awssdk.services.dynamodb.model.KeyType;
import software.amazon.awssdk.services.dynamodb.model.CreateTableResponse;
import software.amazon.awssdk.services.dynamodb.model.DescribeTableRequest;
import software.amazon.awssdk.services.dynamodb.model.DescribeTableResponse;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.waiters.DynamoDbWaiter;
```

 **Code** 

```
    public static String createTable(DynamoDbClient ddb, String tableName, String key) {

        DynamoDbWaiter dbWaiter = ddb.waiter();
        CreateTableRequest request = CreateTableRequest.builder()
                .attributeDefinitions(AttributeDefinition.builder()
                        .attributeName(key)
                        .attributeType(ScalarAttributeType.S)
                        .build())
                .keySchema(KeySchemaElement.builder()
                        .attributeName(key)
                        .keyType(KeyType.HASH)
                        .build())
                .provisionedThroughput(ProvisionedThroughput.builder()
                        .readCapacityUnits(new Long(10))
                        .writeCapacityUnits(new Long(10))
                        .build())
                .tableName(tableName)
                .build();

        String newTable ="";
        try {
            CreateTableResponse response = ddb.createTable(request);
            DescribeTableRequest tableRequest = DescribeTableRequest.builder()
                    .tableName(tableName)
                    .build();

            // Wait until the Amazon DynamoDB table is created
            WaiterResponse<DescribeTableResponse> waiterResponse =  dbWaiter.waitUntilTableExists(tableRequest);
            waiterResponse.matched().response().ifPresent(System.out::println);

            newTable = response.tableDescription().tableName();
            return newTable;

        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }
       return "";
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/dynamodb/src/main/java/com/example/dynamodb/CreateTable.java)。

### 使用複合主索引鍵建立資料表
<a name="dynamodb-create-table-composite"></a>

下列範例會建立具有兩個屬性的資料表。這兩個屬性都用於複合主索引鍵。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest;
import software.amazon.awssdk.services.dynamodb.model.CreateTableResponse;
import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement;
import software.amazon.awssdk.services.dynamodb.model.KeyType;
import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput;
import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
```

 **Code** 

```
    public static String createTableComKey(DynamoDbClient ddb, String tableName) {
        CreateTableRequest request = CreateTableRequest.builder()
                .attributeDefinitions(
                        AttributeDefinition.builder()
                                .attributeName("Language")
                                .attributeType(ScalarAttributeType.S)
                                .build(),
                        AttributeDefinition.builder()
                                .attributeName("Greeting")
                                .attributeType(ScalarAttributeType.S)
                                .build())
                .keySchema(
                        KeySchemaElement.builder()
                                .attributeName("Language")
                                .keyType(KeyType.HASH)
                                .build(),
                        KeySchemaElement.builder()
                                .attributeName("Greeting")
                                .keyType(KeyType.RANGE)
                                .build())
                .provisionedThroughput(
                        ProvisionedThroughput.builder()
                                .readCapacityUnits(new Long(10))
                                .writeCapacityUnits(new Long(10)).build())
                .tableName(tableName)
                .build();

       String tableId = "";

       try {
            CreateTableResponse result = ddb.createTable(request);
            tableId = result.tableDescription().tableId();
            return tableId;
        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }
       return "";
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/dynamodb/src/main/java/com/example/dynamodb/CreateTableCompositeKey.java)。

## 列出資料表
<a name="dynamodb-list-tables"></a>

您可以透過呼叫 `DynamoDbClient’s``listTables`方法列出特定區域中的資料表。

**注意**  
如果您的帳戶和區域不存在具名資料表，[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ResourceNotFoundException.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ResourceNotFoundException.html)則會擲回 。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.model.ListTablesResponse;
import software.amazon.awssdk.services.dynamodb.model.ListTablesRequest;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import java.util.List;
```

 **Code** 

```
    public static void listAllTables(DynamoDbClient ddb){

        boolean moreTables = true;
        String lastName = null;

        while(moreTables) {
            try {
                ListTablesResponse response = null;
                if (lastName == null) {
                    ListTablesRequest request = ListTablesRequest.builder().build();
                    response = ddb.listTables(request);
                } else {
                    ListTablesRequest request = ListTablesRequest.builder()
                            .exclusiveStartTableName(lastName).build();
                    response = ddb.listTables(request);
                }

                List<String> tableNames = response.tableNames();

                if (tableNames.size() > 0) {
                    for (String curName : tableNames) {
                        System.out.format("* %s\n", curName);
                    }
                } else {
                    System.out.println("No tables found!");
                    System.exit(0);
                }

                lastName = response.lastEvaluatedTableName();
                if (lastName == null) {
                    moreTables = false;
                }
            } catch (DynamoDbException e) {
                System.err.println(e.getMessage());
                System.exit(1);
            }
        }
        System.out.println("\nDone!");
    }
```

根據預設，每次呼叫最多傳回 100 個資料表 - 在傳回的[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ListTablesResponse.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ListTablesResponse.html)物件`lastEvaluatedTableName`上使用 以取得上次評估的資料表。您可以使用這個值，在前次列表最後傳回值之後開始列表。

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/dynamodb/src/main/java/com/example/dynamodb/ListTables.java)。

## 描述資料表 (取得其相關資訊)
<a name="dynamodb-describe-table"></a>

使用 `DynamoDbClient’s``describeTable`方法取得資料表的相關資訊。

**注意**  
如果您的帳戶和區域不存在具名資料表，[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ResourceNotFoundException.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ResourceNotFoundException.html)則會擲出 。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
import software.amazon.awssdk.services.dynamodb.model.DescribeTableRequest;
import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughputDescription;
import software.amazon.awssdk.services.dynamodb.model.TableDescription;
import java.util.List;
```

 **Code** 

```
    public static void describeDymamoDBTable(DynamoDbClient ddb,String tableName ) {

        DescribeTableRequest request = DescribeTableRequest.builder()
                .tableName(tableName)
                .build();

        try {
            TableDescription tableInfo =
                    ddb.describeTable(request).table();

            if (tableInfo != null) {
                System.out.format("Table name  : %s\n",
                        tableInfo.tableName());
                System.out.format("Table ARN   : %s\n",
                        tableInfo.tableArn());
                System.out.format("Status      : %s\n",
                        tableInfo.tableStatus());
                System.out.format("Item count  : %d\n",
                        tableInfo.itemCount().longValue());
                System.out.format("Size (bytes): %d\n",
                        tableInfo.tableSizeBytes().longValue());

                ProvisionedThroughputDescription throughputInfo =
                        tableInfo.provisionedThroughput();
                System.out.println("Throughput");
                System.out.format("  Read Capacity : %d\n",
                        throughputInfo.readCapacityUnits().longValue());
                System.out.format("  Write Capacity: %d\n",
                        throughputInfo.writeCapacityUnits().longValue());

                List<AttributeDefinition> attributes =
                        tableInfo.attributeDefinitions();
                System.out.println("Attributes");

                for (AttributeDefinition a : attributes) {
                    System.out.format("  %s (%s)\n",
                            a.attributeName(), a.attributeType());
                }
            }
        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }
        System.out.println("\nDone!");
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/dynamodb/src/main/java/com/example/dynamodb/DescribeTable.java)。

## 修改 (更新) 資料表
<a name="dynamodb-update-table"></a>

您可以隨時呼叫 `DynamoDbClient’s``updateTable`方法修改資料表的佈建輸送量值。

**注意**  
如果您的 帳戶和區域不存在具名資料表，則會擲回 [ResourceNotFoundException](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ResourceNotFoundException.html)。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.UpdateTableRequest;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
```

 **Code** 

```
    public static void updateDynamoDBTable(DynamoDbClient ddb,
                                           String tableName,
                                           Long readCapacity,
                                           Long writeCapacity) {

        System.out.format(
                "Updating %s with new provisioned throughput values\n",
                tableName);
        System.out.format("Read capacity : %d\n", readCapacity);
        System.out.format("Write capacity : %d\n", writeCapacity);

        ProvisionedThroughput tableThroughput = ProvisionedThroughput.builder()
                .readCapacityUnits(readCapacity)
                .writeCapacityUnits(writeCapacity)
                .build();

        UpdateTableRequest request = UpdateTableRequest.builder()
                .provisionedThroughput(tableThroughput)
                .tableName(tableName)
                .build();

        try {
            ddb.updateTable(request);
        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }

        System.out.println("Done!");
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/dynamodb/src/main/java/com/example/dynamodb/UpdateTable.java)。

## 刪除資料表
<a name="dynamodb-delete-table"></a>

若要刪除資料表，請呼叫 `DynamoDbClient’s` `deleteTable` 方法並提供資料表的名稱。

**注意**  
如果您的帳戶和區域不存在具名資料表，則會擲回 [ResourceNotFoundException](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ResourceNotFoundException.html)。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest;
```

 **Code** 

```
    public static void deleteDynamoDBTable(DynamoDbClient ddb, String tableName) {

        DeleteTableRequest request = DeleteTableRequest.builder()
                .tableName(tableName)
                .build();

        try {
            ddb.deleteTable(request);

        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }
        System.out.println(tableName +" was successfully deleted!");
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/dynamodb/src/main/java/com/example/dynamodb/DeleteTable.java)。

## 其他資訊
<a name="more-information"></a>
+  《 Amazon DynamoDB 開發人員指南》中的[使用資料表的指導方針](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GuidelinesForTables.html) 
+  《 Amazon DynamoDB 開發人員指南》中的[在 中使用資料表 DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/WorkingWithTables.html) 

# 在 中使用項目 DynamoDB
<a name="examples-dynamodb-items"></a>

在 中 DynamoDB，項目是*屬性*的集合，每個屬性都有*名稱*和*值*。屬性值可以是純量、集合或文件類型。如需詳細資訊，請參閱《 開發人員指南》中的 Amazon DynamoDB [命名規則和資料類型](https://docs.aws.amazon.com//amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html)。

## 從資料表擷取 (取得) 項目
<a name="dynamodb-get-item"></a>

呼叫 DynamoDbClient 的 `getItem`方法，並傳遞具有所需項目資料表名稱和主索引鍵值的 [GetItemRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/GetItemRequest.html) 物件。它會傳回 [GetItemResponse](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/GetItemResponse.html) 物件，包含該項目的所有屬性。您可以在 [ 中指定一或多個](https://docs.aws.amazon.com//amazondynamodb/latest/developerguide/Expressions.ProjectionExpressions.html)投射運算式`GetItemRequest`來擷取特定的屬性。

您可以使用所傳回 `GetItemResponse` 物件的 `item()` 方法來擷取與項目關聯之索引鍵 (字串) 和值的[對應](https://docs.oracle.com/javase/8/docs/api/index.html?java/util/Map.html) ([AttributeValue](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/AttributeValue.html)) 組。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.GetItemRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
```

 **Code** 

```
    public static void getDynamoDBItem(DynamoDbClient ddb,String tableName,String key,String keyVal ) {

        HashMap<String,AttributeValue> keyToGet = new HashMap<String,AttributeValue>();

        keyToGet.put(key, AttributeValue.builder()
                .s(keyVal).build());

        GetItemRequest request = GetItemRequest.builder()
                .key(keyToGet)
                .tableName(tableName)
                .build();

        try {
            Map<String,AttributeValue> returnedItem = ddb.getItem(request).item();

            if (returnedItem != null) {
                Set<String> keys = returnedItem.keySet();
                System.out.println("Amazon DynamoDB table attributes: \n");

                for (String key1 : keys) {
                    System.out.format("%s: %s\n", key1, returnedItem.get(key1).toString());
                }
            } else {
                System.out.format("No item found with the key %s!\n", key);
            }
        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/bc964a243276990f05c180618ea8b34777c68f0e/javav2/example_code/dynamodb/src/main/java/com/example/dynamodb/GetItem.java)。

## 使用非同步用戶端從資料表擷取 (取得) 項目
<a name="id1ddb"></a>

叫用 DynamoDbAsyncClient 的 `getItem`方法，並傳遞具有所需項目之資料表名稱和主索引鍵值的 [GetItemRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/GetItemRequest.html) 物件。

您可以傳回一個[集合](https://docs.oracle.com/javase/8/docs/api/index.html?java/util/Collection.html)執行個體，其中包含該項目的所有屬性 (請參閱以下範例)。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.model.GetItemRequest;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
```

 **Code** 

```
    public static void getItem(DynamoDbAsyncClient client, String tableName, String key,  String keyVal) {

        HashMap<String, AttributeValue> keyToGet =
                new HashMap<String, AttributeValue>();

        keyToGet.put(key, AttributeValue.builder()
                .s(keyVal).build());

        try {

            // Create a GetItemRequest instance
            GetItemRequest request = GetItemRequest.builder()
                    .key(keyToGet)
                    .tableName(tableName)
                    .build();

            // Invoke the DynamoDbAsyncClient object's getItem
            java.util.Collection<AttributeValue> returnedItem = client.getItem(request).join().item().values();

            // Convert Set to Map
            Map<String, AttributeValue> map = returnedItem.stream().collect(Collectors.toMap(AttributeValue::s, s->s));
            Set<String> keys = map.keySet();
            for (String sinKey : keys) {
                System.out.format("%s: %s\n", sinKey, map.get(sinKey).toString());
            }

        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/bc964a243276990f05c180618ea8b34777c68f0e/javav2/example_code/dynamodbasync/src/main/java/com/example/dynamodbasync/DynamoDBAsyncGetItem.java)。

## 將新項目新增至資料表
<a name="dynamodb-add-item"></a>

建立代表項目屬性的索引鍵值組[對應](https://docs.oracle.com/javase/8/docs/api/index.html?java/util/Map.html)。這些項目必須包含資料表主索引鍵欄位的值。如果主索引鍵識別的項目已存在，其欄位會透過請求*更新*。

**注意**  
如果您的帳戶和區域不存在指定的資料表，會擲出 [ResourceNotFoundException](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ResourceNotFoundException.html)。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.PutItemRequest;
import software.amazon.awssdk.services.dynamodb.model.ResourceNotFoundException;
import java.util.HashMap;
```

 **Code** 

```
    public static void putItemInTable(DynamoDbClient ddb,
                                      String tableName,
                                      String key,
                                      String keyVal,
                                      String albumTitle,
                                      String albumTitleValue,
                                      String awards,
                                      String awardVal,
                                      String songTitle,
                                      String songTitleVal){

        HashMap<String,AttributeValue> itemValues = new HashMap<String,AttributeValue>();

        // Add all content to the table
        itemValues.put(key, AttributeValue.builder().s(keyVal).build());
        itemValues.put(songTitle, AttributeValue.builder().s(songTitleVal).build());
        itemValues.put(albumTitle, AttributeValue.builder().s(albumTitleValue).build());
        itemValues.put(awards, AttributeValue.builder().s(awardVal).build());

        PutItemRequest request = PutItemRequest.builder()
                .tableName(tableName)
                .item(itemValues)
                .build();

        try {
            ddb.putItem(request);
            System.out.println(tableName +" was successfully updated");

        } catch (ResourceNotFoundException e) {
            System.err.format("Error: The Amazon DynamoDB table \"%s\" can't be found.\n", tableName);
            System.err.println("Be sure that it exists and that you've typed its name correctly!");
            System.exit(1);
        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f4eaf2b2971805cfb2b87a8e5ab408f83169432e/javav2/example_code/dynamodb/src/main/java/com/example/dynamodb/PutItem.java)。

## 更新資料表中的現有項目
<a name="dynamodb-update-item"></a>

您可以使用 DynamoDbClient 的 `updateItem`方法，提供資料表名稱、主索引鍵值和要更新的欄位映射，來更新已存在於資料表中的項目屬性。

**注意**  
如果您的帳戶和區域不存在指定的資料表，或者如果您傳遞的主索引鍵所識別的項目不存在，會擲出 [ResourceNotFoundException](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ResourceNotFoundException.html)。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.model.AttributeAction;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.AttributeValueUpdate;
import software.amazon.awssdk.services.dynamodb.model.ResourceNotFoundException;
import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import java.util.HashMap;
```

 **Code** 

```
    public static void updateTableItem(DynamoDbClient ddb,
                                       String tableName,
                                       String key,
                                       String keyVal,
                                       String name,
                                       String updateVal){

        HashMap<String,AttributeValue> itemKey = new HashMap<String,AttributeValue>();

        itemKey.put(key, AttributeValue.builder().s(keyVal).build());

        HashMap<String,AttributeValueUpdate> updatedValues =
                new HashMap<String,AttributeValueUpdate>();

        // Update the column specified by name with updatedVal
        updatedValues.put(name, AttributeValueUpdate.builder()
                .value(AttributeValue.builder().s(updateVal).build())
                .action(AttributeAction.PUT)
                .build());

        UpdateItemRequest request = UpdateItemRequest.builder()
                .tableName(tableName)
                .key(itemKey)
                .attributeUpdates(updatedValues)
                .build();

        try {
            ddb.updateItem(request);
        } catch (ResourceNotFoundException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }

        System.out.println("Done!");
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f4eaf2b2971805cfb2b87a8e5ab408f83169432e/javav2/example_code/dynamodb/src/main/java/com/example/dynamodb/UpdateItem.java)。

## 刪除資料表中的現有項目
<a name="dynamodb-delete-item"></a>

您可以使用 DynamoDbClient 的 `deleteItem`方法並提供資料表名稱和主索引鍵值，來刪除存在於資料表中的項目。

**注意**  
如果您的帳戶和區域不存在指定的資料表，或者如果您傳遞的主索引鍵所識別的項目不存在，會擲出 [ResourceNotFoundException](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ResourceNotFoundException.html)。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.DeleteItemRequest;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import java.util.HashMap;
```

 **Code** 

```
  public static void deleteDynamoDBItem(DynamoDbClient ddb, String tableName, String key, String keyVal) {

        HashMap<String,AttributeValue> keyToGet =
                new HashMap<String,AttributeValue>();

        keyToGet.put(key, AttributeValue.builder()
                .s(keyVal)
                .build());

        DeleteItemRequest deleteReq = DeleteItemRequest.builder()
                .tableName(tableName)
                .key(keyToGet)
                .build();

        try {
            ddb.deleteItem(deleteReq);
        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f4eaf2b2971805cfb2b87a8e5ab408f83169432e/javav2/example_code/dynamodb/src/main/java/com/example/dynamodb/DeleteItem.java)。

## 其他資訊
<a name="more-information"></a>
+  《 Amazon DynamoDB 開發人員指南》中的[使用項目的指導方針](https://docs.aws.amazon.com//amazondynamodb/latest/developerguide/best-practices.html) 
+  《 Amazon DynamoDB 開發人員指南》中的[使用 中的項目 DynamoDB](https://docs.aws.amazon.com//amazondynamodb/latest/developerguide/WorkingWithItems.html) 

# 使用 將 Java 物件映射至 DynamoDB 項目 AWS SDK for Java 2.x
<a name="dynamodb-enhanced-client"></a>

[DynamoDB 增強型用戶端 API](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/package-summary.html) 是高階程式庫，是適用於 Java 的 SDK v1.x 中 `DynamoDBMapper`類別的後續版本。提供將客戶端類別映射到 DynamoDB 資料表的直接方式。您可以在程式碼中定義資料表與其對應資料類別之間的關係。定義關係之後，您可以直觀地對 DynamoDB 中的資料表或項目執行各種建立、讀取、更新或刪除 (CRUD) 操作。

DynamoDB 增強型用戶端 API 也包含[增強型文件 API](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/document/package-summary.html)，可讓您使用未遵循定義結構描述的文件類型項目。

**Topics**
+ [開始使用 DynamoDB 增強型用戶端 API](ddb-en-client-getting-started.md)
+ [了解 DynamoDB 增強型用戶端 API 的基本概念](ddb-en-client-use.md)
+ [使用進階映射功能](ddb-en-client-adv-features.md)
+ [使用適用於 DynamoDB 的增強型文件 API 處理 JSON 文件](ddb-en-client-doc-api.md)
+ [使用擴充功能自訂 DynamoDB 增強型用戶端操作](ddb-en-client-extensions.md)
+ [非同步使用 DynamoDB 增強型用戶端 API](ddb-en-client-async.md)
+ [資料類別註釋](ddb-en-client-anno-index.md)

# 開始使用 DynamoDB 增強型用戶端 API
<a name="ddb-en-client-getting-started"></a>

以下教學課程向您介紹使用 DynamoDB 增強型用戶端 API 所需的基本概念。

## 新增相依性
<a name="ddb-en-client-gs-dep"></a>

若要開始在專案中使用 DynamoDB 增強型用戶端 API，請在 `dynamodb-enhanced` Maven 成品上新增相依性。這會顯示在下列範例中。

------
#### [ Maven ]

```
<project>
  <dependencyManagement>
   <dependencies>
      <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>bom</artifactId>
        <version><VERSION></version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
   </dependencies>
  </dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>software.amazon.awssdk</groupId>
      <artifactId>dynamodb-enhanced</artifactId>
    </dependency>
  </dependencies>
  ...
</project>
```

對 Maven 中央儲存庫執行[最新版本](https://central.sonatype.com/artifact/software.amazon.awssdk/bom)的搜尋，並以此值取代 *<VERSION>*。

------
#### [ Gradle ]

```
repositories {
    mavenCentral()
}
dependencies {
    implementation(platform("software.amazon.awssdk:bom:<VERSION>"))
    implementation("software.amazon.awssdk:dynamodb-enhanced")
    ...
}
```

對 Maven 中央儲存庫執行[最新版本](https://central.sonatype.com/artifact/software.amazon.awssdk/bom)的搜尋，並以此值取代 *<VERSION>*。

------

# `TableSchema` 從資料類別產生
<a name="ddb-en-client-gs-tableschema"></a>

`[TableSchema](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/TableSchema.html)` 可讓增強型用戶端在用戶端類別之間映射 DynamoDB 屬性值。在本教學課程中，您將了解衍生自靜態資料類別並使用建置器從程式碼產生的 `TableSchema`。

## 使用標註的資料類別
<a name="ddb-en-client-gs-tableschema-anno-bean"></a>

適用於 Java 的 SDK 2.x 包含[一組註釋](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/annotations/package-summary.html)，您可以搭配資料類別使用，以快速產生 `TableSchema`，將您的類別映射至資料表。

首先建立符合 [JavaBean 規格](https://download.oracle.com/otn-pub/jcp/7224-javabeans-1.01-fr-spec-oth-JSpec/beans.101.pdf)的資料類別。規格要求 類別具有無引數的公有建構函數，並具有類別中每個屬性的 getter 和 setter。包含類別層級註釋，表示資料類別是 `DynamoDbBean`。此外，在 getter 或 setter 上至少包含主索引鍵屬性的`DynamoDbPartitionKey`註釋。

您可以將[屬性層級註釋](ddb-en-client-anno-index.md)套用至 getter 或 setter，但不能同時套用兩者。

**注意**  
此術語`property`通常用於封裝在 JavaBean 中的值。不過，本指南會`attribute`改用 一詞，以符合 DynamoDB 所使用的術語。

下列`Customer`類別顯示將類別定義連結至 DynamoDB 資料表的註釋。

### `Customer` 類別
<a name="ddb-en-client-gs-tableschema-anno-bean-cust"></a>

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

import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSortKey;

import java.time.Instant;

@DynamoDbBean
public class Customer {

    private String id;
    private String name;
    private String email;
    private Instant regDate;

    @DynamoDbPartitionKey
    public String getId() { return this.id; }

    public void setId(String id) { this.id = id; }

    public String getCustName() { return this.name; }

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

    @DynamoDbSortKey
    public String getEmail() { return this.email; }

    public void setEmail(String email) { this.email = email; }

    public Instant getRegistrationDate() { return this.regDate; }

    public void setRegistrationDate(Instant registrationDate) { this.regDate = registrationDate; }

    @Override
    public String toString() {
        return "Customer [id=" + id + ", name=" + name + ", email=" + email
                + ", regDate=" + regDate + "]";
    }
}
```

建立註釋的資料類別之後，請使用它來建立 `TableSchema`，如下列程式碼片段所示。

```
static final TableSchema<Customer> customerTableSchema = TableSchema.fromBean(Customer.class);
```

`TableSchema` 的設計是靜態且不變的。您通常可以在類別載入時間將其執行個體化。

靜態`TableSchema.fromBean()`原廠方法會向 Bean 自我檢查，以產生 DynamoDB 屬性之間資料類別屬性 （屬性） 的映射。

如需使用由數個資料類別組成之資料模型的範例，請參閱 [使用 Bean、地圖、清單和集合等屬性](ddb-en-client-adv-features-nested.md)區段中的 `Person`類別。

## 使用建置器
<a name="ddb-en-client-gs-tableschema-builder"></a>

如果您在程式碼中定義資料表結構描述，則可以略過 Bean 自我檢查的成本。如果您編寫結構描述的程式碼，您的類別就不需要遵循 JavaBean 命名標準，也不需要加上註釋。下列範例使用建置器，相當於使用註釋的`Customer`類別範例。

```
static final TableSchema<Customer> customerTableSchema =
                TableSchema.builder(Customer.class)
                        .newItemSupplier(Customer::new)
                        .addAttribute(String.class, a -> a.name("id")
                                .getter(Customer::getId)
                                .setter(Customer::setId)
                                .tags(StaticAttributeTags.primaryPartitionKey()))
                        .addAttribute(String.class, a -> a.name("email")
                                .getter(Customer::getEmail)
                                .setter(Customer::setEmail)
                                .tags(StaticAttributeTags.primarySortKey()))
                        .addAttribute(String.class, a -> a.name("name")
                                .getter(Customer::getCustName)
                                .setter(Customer::setCustName))
                        .addAttribute(Instant.class, a -> a.name("registrationDate")
                                .getter(Customer::getRegistrationDate)
                                .setter(Customer::setRegistrationDate))
                        .build();
```

# 建立增強型用戶端和 `DynamoDbTable`
<a name="ddb-en-client-getting-started-dynamodbTable"></a>

## 建立增強型用戶端
<a name="ddb-en-client-getting-started-dynamodbTable-eclient"></a>

[DynamoDbEnhancedClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html) 類別或其非同步對應 [DynamoDbEnhancedAsyncClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedAsyncClient.html) 是使用 DynamoDB 增強型用戶端 API 的進入點。

增強型用戶端需要標準 `[DynamoDbClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html)`才能執行工作。API 提供兩種建立`DynamoDbEnhancedClient`執行個體的方式。下列程式碼片段中顯示的第一個選項會建立標準`DynamoDbClient`，其中包含從組態設定中挑選的預設設定。

```
DynamoDbEnhancedClient enhancedClient = DynamoDbEnhancedClient.create();
```

如果您想要設定基礎標準用戶端，您可以將其提供給增強型用戶端的建置器方法，如下列程式碼片段所示。

```
// Configure an instance of the standard DynamoDbClient.
DynamoDbClient standardClient = DynamoDbClient.builder()
    .region(Region.US_EAST_1)
    .credentialsProvider(ProfileCredentialsProvider.create())
    .build();

// Use the configured standard client with the enhanced client.
DynamoDbEnhancedClient enhancedClient = DynamoDbEnhancedClient.builder()
    .dynamoDbClient(standardClient)
    .build();
```

## 建立 `DynamoDbTable` 執行個體
<a name="ddb-en-client-getting-started-dynamodbTable-table"></a>

將 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbTable.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbTable.html)視為 DynamoDB 資料表的用戶端表示法，該資料表使用 提供的映射功能`TableSchema`。`DynamoDbTable` 類別提供 CRUD 操作的方法，可讓您與單一 DynamoDB 資料表互動。

`DynamoDbTable<T>` 是採用單一類型引數的一般類別，無論是自訂類別，還是使用文件類型項目`EnhancedDocument`時的 。此引數類型會建立您使用的類別與單一 DynamoDB 資料表之間的關係。

使用 的`table()`原廠方法`DynamoDbEnhancedClient`建立`DynamoDbTable`執行個體，如下列程式碼片段所示。

```
static final DynamoDbTable<Customer> customerTable = 
        enhancedClient.table("Customer", TableSchema.fromBean(Customer.class));
```

`DynamoDbTable` 執行個體是單調的候選項目，因為它們不可變，可用於整個應用程式。

您的程式碼現在具有可使用`Customer`執行個體的 DynamoDB 資料表的記憶體內表示法。實際的 DynamoDB 資料表可能存在，也可能不存在。如果名為 的資料表`Customer`已存在，您可以開始對其執行 CRUD 操作。如果不存在，請使用`DynamoDbTable`執行個體來建立資料表，如下一節所述。

# 視需要建立 DynamoDB 資料表
<a name="ddb-en-client-gs-ddbtable"></a>

建立`DynamoDbTable`執行個體之後，請使用它在 DynamoDB 中執行資料表的*一次性*建立。

## 建立資料表範例程式碼
<a name="ddb-en-client-gs-ddbtable-createex"></a>

下列範例會根據`Customer`資料類別建立 DynamoDB 資料表。

此範例會使用與類別名稱`Customer`相同的名稱建立 DynamoDB 資料表，但資料表名稱可以是其他名稱。無論您如何命名資料表，您都必須在其他應用程式中使用此名稱，才能使用資料表。每當您建立另一個`DynamoDbTable`物件時，請將此名稱提供給 `table()`方法，以便使用基礎 DynamoDB 資料表。

Java lambda 參數 `builder`傳遞至 `createTable`方法可讓您[自訂資料表](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/CreateTableEnhancedRequest.Builder.html)。在此範例中，已設定[佈建輸送量](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ReadWriteCapacityMode.html#HowItWorks.ProvisionedThroughput.Manual)。如果您想要在建立資料表時使用預設設定，請略過建置器，如下列程式碼片段所示。

```
customerTable.createTable();
```

使用預設設定時，不會設定佈建輸送量的值。而是將資料表的計費模式設定為[隨需](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ReadWriteCapacityMode.html#HowItWorks.OnDemand)。

此範例也會在嘗試列印回應中收到的資料表名稱`[DynamoDbWaiter](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/waiters/DynamoDbWaiter.html)`之前，使用 。建立資料表需要一些時間。因此，使用等待程式表示您不需要撰寫輪詢 DynamoDB 服務的邏輯，即可在使用資料表之前查看資料表是否存在。

### 匯入
<a name="ddb-en-client-gs-ddbtable-imports"></a>

```
import com.example.dynamodb.Customer;
import software.amazon.awssdk.core.internal.waiters.ResponseOrException;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable;
import software.amazon.awssdk.enhanced.dynamodb.TableSchema;
import software.amazon.awssdk.enhanced.dynamodb.model.CreateTableEnhancedRequest;
import software.amazon.awssdk.services.dynamodb.model.DescribeTableResponse;
import software.amazon.awssdk.services.dynamodb.waiters.DynamoDbWaiter;
```

### Code
<a name="ddb-en-client-gs-ddbtable-code"></a>

```
 public static void createCustomerTable(DynamoDbTable<Customer> customerTable, DynamoDbClient standardClient) {
     // Create the DynamoDB table using the 'customerTable' DynamoDbTable instance.
     customerTable.createTable(builder -> builder
             .provisionedThroughput(b -> b
                     .readCapacityUnits(10L)
                     .writeCapacityUnits(10L)
                     .build())
     );
     // The DynamoDbClient instance (named 'standardClient') passed to the builder for the DynamoDbWaiter is the same instance
     // that was passed to the builder of the DynamoDbEnhancedClient instance that we created previously.
     // By using the same instance, it ensures that the same Region that was configured on the standard DynamoDbClient 
     // instance is used for other service clients that accept a DynamoDbClient during construction.
     try (DynamoDbWaiter waiter = DynamoDbWaiter.builder().client(standardClient).build()) { // DynamoDbWaiter is Autocloseable
         ResponseOrException<DescribeTableResponse> response = waiter
                 .waitUntilTableExists(builder -> builder.tableName("Customer").build())
                 .matched();
         DescribeTableResponse tableDescription = response.response().orElseThrow(
                 () -> new RuntimeException("Customer table was not created."));
         // The actual error can be inspected in response.exception()
         logger.info("Customer table was created.");
     }
 }
```

**注意**  
從資料類別產生資料表時，DynamoDB 資料表的屬性名稱會以小寫字母開頭。如果您希望資料表的屬性名稱以大寫字母開頭，請使用 [`@DynamoDbAttribute(NAME)`註釋](ddb-en-client-adv-features-inex-attr.md)，並提供您想要做為參數的名稱。

# 執行 操作
<a name="ddb-en-client-gs-use"></a>

建立資料表之後，請使用`DynamoDbTable`執行個體對 DynamoDB 資料表執行操作。

在下列範例中，單一項目`DynamoDbTable<Customer>`會與[`Customer`資料類別](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-anno-bean-cust)執行個體一起做為參數傳遞，以將新項目新增至資料表。

```
    public static void putItemExample(DynamoDbTable<Customer> customerTable, Customer customer){
        logger.info(customer.toString());
        customerTable.putItem(customer);
    }
```

## `Customer` 物件
<a name="perform_ops_create_customer_instatnce"></a>

```
        Customer customer = new Customer();
        customer.setId("1");
        customer.setCustName("Customer Name");
        customer.setEmail("customer@example.com");
        customer.setRegistrationDate(Instant.parse("2023-07-03T10:15:30.00Z"));
```

將`customer`物件傳送至 DynamoDB 服務之前，請記錄物件`toString()`方法的輸出，將其與增強型用戶端傳送的內容進行比較。

```
Customer [id=1, name=Customer Name, email=customer@example.com, regDate=2023-07-03T10:15:30Z]
```

線路層級記錄會顯示所產生請求的承載。增強型用戶端會從資料類別產生低階表示。`regDate` 屬性是 Java 中的`Instant`類型，以 DynamoDB 字串表示。

```
{
  "TableName": "Customer",
  "Item": {
    "registrationDate": {
      "S": "2023-07-03T10:15:30Z"
    },
    "id": {
      "S": "1"
    },
    "custName": {
      "S": "Customer Name"
    },
    "email": {
      "S": "customer@example.com"
    }
  }
}
```

# 使用現有資料表
<a name="ddb-en-client-gs-existingtable"></a>

上一節說明如何從 Java 資料類別開始建立 DynamoDB 資料表。如果您已經有現有的資料表，並想要使用增強型用戶端的功能，您可以建立 Java 資料類別來使用資料表。您需要檢查 DynamoDB 資料表，並將必要的註釋新增至資料類別。

在使用現有資料表之前，請呼叫 `DynamoDbEnhanced.table()`方法。這在上一個範例中使用下列陳述式完成。

```
DynamoDbTable<Customer> customerTable = enhancedClient.table("Customer", TableSchema.fromBean(Customer.class));
```

傳回`DynamoDbTable`執行個體之後，您可以立即開始使用基礎資料表。您不需要透過呼叫 `DynamoDbTable.createTable()`方法重新建立資料表。

下列範例示範如何立即從 DynamoDB 資料表擷取`Customer`執行個體。

```
DynamoDbTable<Customer> customerTable = enhancedClient.table("Customer", TableSchema.fromBean(Customer.class));
// The Customer table exists already and has an item with a primary key value of "1" and a sort key value of "customer@example.com".
customerTable.getItem(
        Key.builder().
                partitionValue("1").
                sortValue("customer@example.com").build());
```

**重要**  
`table()` 方法中使用的資料表名稱必須符合現有的 DynamoDB 資料表名稱。

# 了解 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());
    }
```

# 使用不可變的資料類別
<a name="ddb-en-client-use-immut"></a>

DynamoDB 增強型用戶端 API 的映射功能適用於不可變的資料類別。不可變類別只有取得器，且需要 SDK 用來建立類別執行個體的建置器類別。不像[客戶類別](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-anno-bean-cust)中所示使用`@DynamoDbBean`註釋，不可變類別會使用`@DynamoDbImmutable`註釋，這會採用參數來指示要使用的建置器類別。

下列類別是 的不可變版本`Customer`。

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

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.DynamoDbSecondarySortKey;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSortKey;

import java.time.Instant;

@DynamoDbImmutable(builder = CustomerImmutable.Builder.class)
public class CustomerImmutable {
    private final String id;
    private final String name;
    private final String email;
    private final Instant regDate;

    private CustomerImmutable(Builder b) {
        this.id = b.id;
        this.email = b.email;
        this.name = b.name;
        this.regDate = b.regDate;
    }

    // This method will be automatically discovered and used by the TableSchema.
    public static Builder builder() { return new Builder(); }

    @DynamoDbPartitionKey
    public String id() { return this.id; }

    @DynamoDbSortKey
    public String email() { return this.email; }

    @DynamoDbSecondaryPartitionKey(indexNames = "customers_by_name")
    public String name() { return this.name; }

    @DynamoDbSecondarySortKey(indexNames = {"customers_by_date", "customers_by_name"})
    public Instant regDate() { return this.regDate; }

    public static final class Builder {
        private String id;
        private String email;
        private String name;
        private Instant regDate;

        // The private Builder constructor is visible to the enclosing CustomerImmutable class.
        private Builder() {}

        public Builder id(String id) { this.id = id; return this; }
        public Builder email(String email) { this.email = email; return this; }
        public Builder name(String name) { this.name = name; return this; }
        public Builder regDate(Instant regDate) { this.regDate = regDate; return this; }

        // This method will be automatically discovered and used by the TableSchema.
        public CustomerImmutable build() { return new CustomerImmutable(this); }
    }
}
```

當您使用 註釋資料類別時，必須符合下列要求`@DynamoDbImmutable`。

1. 不是 覆寫`Object.class`且尚未加上註釋的每個方法，`@DynamoDbIgnore`都必須是 DynamoDB 資料表屬性的 getter。

1. 每個 getter 在建置器類別上都必須有對應的區分大小寫設定器。

1. 僅必須符合下列其中一項建構條件。
   + 建置器類別必須具有公有預設建構函數。
   + 資料類別必須具有名為 的公有靜態方法`builder()`，不採用任何參數，並傳回建置器類別的執行個體。此選項會顯示在不可變類別中`Customer`。

1.  建置器類別必須具有名為 的公有方法`build()`，不採用任何參數，並傳回不可變類別的執行個體。

若要`TableSchema`為您的不可變類別建立 ，請使用 上的 `fromImmutableClass()`方法`TableSchema`，如下列程式碼片段所示。

```
static final TableSchema<CustomerImmutable> customerImmutableTableSchema = 
                         TableSchema.fromImmutableClass(CustomerImmutable.class);
```

就像您可以從可變類別建立 DynamoDB 資料表一樣，您可以從不可變類別建立一個資料表，並對 `createTable()` 進行*一次性*呼叫`DynamoDbTable`，如下列程式碼片段範例所示。

```
static void createTableFromImmutable(DynamoDbEnhancedClient enhancedClient, String tableName, DynamoDbWaiter waiter){
    // First, create an in-memory representation of the table using the 'table()' method of the DynamoDb Enhanced Client.
    // 'table()' accepts a name for the table and a TableSchema instance that you created previously.
    DynamoDbTable<CustomerImmutable> customerDynamoDbTable = enhancedClient
            .table(tableName, TableSchema.fromImmutableClass(CustomerImmutable.class));
        
    // Second, call the 'createTable()' method on the DynamoDbTable instance.
    customerDynamoDbTable.createTable();
    waiter.waitUntilTableExists(b -> b.tableName(tableName));
}
```

## 使用第三方程式庫，例如 Lombok
<a name="ddb-en-client-use-immut-lombok"></a>

第三方程式庫，例如 [Project Lombok](https://projectlombok.org/)，可協助產生與不可變物件相關聯的樣板程式碼。只要資料類別遵循本節中詳述的慣例，DynamoDB 增強型用戶端 API 就會使用這些程式庫。

下列範例顯示具有 Lombok 註釋的不可變`CustomerImmutable`類別。請注意，Lombok `onMethod`的功能如何將屬性型 DynamoDB 註釋，例如 `@DynamoDbPartitionKey`，複製到產生的程式碼。

```
@Value
@Builder
@DynamoDbImmutable(builder = Customer.CustomerBuilder.class)
public class Customer {
    @Getter(onMethod_=@DynamoDbPartitionKey)
    private String id;

    @Getter(onMethod_=@DynamoDbSortKey)
    private String email;

    @Getter(onMethod_=@DynamoDbSecondaryPartitionKey(indexNames = "customers_by_name"))
    private String name;

    @Getter(onMethod_=@DynamoDbSecondarySortKey(indexNames = {"customers_by_date", "customers_by_name"}))
    private Instant createdDate;
}
```

# 使用表達式和條件
<a name="ddb-en-client-expressions"></a>

DynamoDB 增強型用戶端 API 中的表達式是 [DynamoDB 表達式](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.html)的 Java 表示法。

DynamoDB 增強型用戶端 API 使用三種類型的表達式：

[運算式](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/Expression.html)  
當您定義條件和篩選條件時，會使用 `Expression`類別。

[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/QueryConditional.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/QueryConditional.html)  
這種類型的表達式代表查詢操作的關鍵[條件](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html#Query.KeyConditionExpressions)。

[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/update/UpdateExpression.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/update/UpdateExpression.html)  
此類別可協助您撰寫 DynamoDB [更新表達式](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html)，且目前在更新項目時用於延伸架構。

## 表達式剖析
<a name="ddb-en-client-expressions-compoonents"></a>

表達式由下列項目組成：
+ 字串表達式 （必要）。字串包含 DynamoDB 邏輯表達式，其中包含屬性名稱和屬性值的預留位置名稱。
+ 表達式值的映射 （通常為必要）。
+ 表達式名稱的映射 （選用）。

使用建置器產生`Expression`採用下列一般形式的物件。

```
Expression expression = Expression.builder()
                            .expression(<String>)
                            .expressionNames(<Map>)
                            .expressionValues(<Map>)
                           .build()
```

`Expression`通常需要表達式值的映射。映射提供字串表達式中預留位置的值。映射金鑰包含前面加上冒號 (`:`) 的預留位置名稱，而映射值是 [AttributeValue](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/AttributeValue.html) 的執行個體。[AttributeValues](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/internal/AttributeValues.html) 類別具有從常值產生`AttributeValue`執行個體的便利方法。或者，您可以使用 `AttributeValue.Builder`來產生`AttributeValue`執行個體。

下列程式碼片段顯示註解行 2 之後有兩個項目的映射。傳遞至 `expression()`方法的字串，顯示於註解行 1 之後，包含 DynamoDB 在執行操作之前解析的預留位置。此程式碼片段不包含表達式名稱的映射，因為*價格*是允許的屬性名稱。

```
    public static void scanAsync(DynamoDbAsyncTable productCatalog) {
        ScanEnhancedRequest request = ScanEnhancedRequest.builder()
                .consistentRead(true)
                .attributesToProject("id", "title", "authors", "price")
                .filterExpression(Expression.builder()
                        // 1. :min_value and :max_value are placeholders for the values provided by the map
                        .expression("price >= :min_value AND price <= :max_value")
                        // 2. Two values are needed for the expression and each is supplied as a map entry.
                        .expressionValues(
                                Map.of( ":min_value", numberValue(8.00),
                                        ":max_value", numberValue(400_000.00)))
                        .build())
                .build();
```

如果 DynamoDB 資料表中的屬性名稱是保留字、以數字開頭或包含空格，則 需要表達式名稱的映射`Expression`。

例如，如果屬性名稱`1price`不是`price`在先前的程式碼範例中，則需要修改範例，如下列範例所示。

```
        ScanEnhancedRequest request = ScanEnhancedRequest.builder()
                .filterExpression(Expression.builder()
                        .expression("#price >= :min_value AND #price <= :max_value")
                        .expressionNames( Map.of("#price", "1price") )
                        .expressionValues(
                                Map.of(":min_value", numberValue(8.00),
                                        ":max_value", numberValue(400_000.00)))
                        .build())
                .build();
```

表達式名稱的預留位置以井號 () 開頭`#`。表達式名稱映射的項目會使用預留位置做為索引鍵，並使用屬性名稱做為值。映射會使用 `expressionNames()`方法新增至表達式建置器。DynamoDB 會在執行操作之前解析屬性名稱。

如果在字串表達式中使用函數，則不需要表達式值。表達式函數的範例為 `attribute_exists(<attribute_name>)`。

下列範例會建置`Expression`使用 [DynamoDB 函數](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html#Expressions.OperatorsAndFunctions.Functions)的 。此範例中的表達式字串不使用任何預留位置。此表達式可用於 `putItem`操作，以檢查資料庫中是否存在屬性`movie`值等於資料物件`movie`屬性的項目。

```
Expression exp = Expression.builder().expression("attribute_not_exists (movie)").build();
```

DynamoDB 開發人員指南包含與 DynamoDB 搭配使用的[低階表達式](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.html)的完整資訊。

## 條件表達式和條件式
<a name="ddb-en-client-expressions-cond"></a>

當您使用 `putItem()`、 `updateItem()`和 `deleteItem()`方法，以及使用交易和批次操作時，您可以使用 `[Expression](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/Expression.html)` 物件來指定 DynamoDB 必須滿足的條件，才能繼續操作。這些表達式會命名為條件表達式。如需範例，請參閱本指南中所示[交易範例](ddb-en-client-use-multiop-trans.md#ddb-en-client-use-multiop-trans-writeitems-opcondition)的 `addDeleteItem()`方法 （註解行 1 之後） 中使用的條件表達式。

當您使用 `query()` 方法時，條件會以 表示[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/QueryConditional.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/QueryConditional.html)。`QueryConditional` 類別有數種靜態便利方法，可協助您撰寫判斷要從 DynamoDB 讀取哪些項目的條件。

如需 的範例`QueryConditionals`，請參閱本指南 [`Query` 方法範例](ddb-en-client-use-multirecord.md#ddb-en-client-use-multirecord-query-example)一節的第一個程式碼範例。

## 篩選條件表達式
<a name="ddb-en-client-expressions-filter"></a>

篩選條件表達式用於掃描和查詢操作，以篩選傳回的項目。

從資料庫讀取所有資料後，就會套用篩選條件表達式，因此讀取成本與沒有篩選條件相同。*Amazon DynamoDB 開發人員指南*提供有關使用篩選條件表達式進行[查詢](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html#Query.FilterExpression)和[掃描](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Scan.html#Scan.FilterExpression)操作的詳細資訊。

下列範例顯示新增至掃描請求的篩選條件表達式。條件會將傳回的項目限制為價格介於 8.00 到 80.00 之間的項目。

```
        Map<String, AttributeValue> expressionValues = Map.of(
                ":min_value", numberValue(8.00),
                ":max_value", numberValue(80.00));

        ScanEnhancedRequest request = ScanEnhancedRequest.builder()
                .consistentRead(true)
                // 1. the 'attributesToProject()' method allows you to specify which values you want returned.
                .attributesToProject("id", "title", "authors", "price")
                // 2. Filter expression limits the items returned that match the provided criteria.
                .filterExpression(Expression.builder()
                        .expression("price >= :min_value AND price <= :max_value")
                        .expressionValues(expressionValues)
                        .build())
                .build();
```

## 更新表達式
<a name="ddb-en-client-expressions-update"></a>

DynamoDB 增強型用戶端的 `updateItem()` 方法提供標準方法來更新 DynamoDB 中的項目。不過，當您需要更多功能時，[UpdateExpressions](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/update/UpdateExpression.html) 會提供 DynamoDB [更新表達式語法的安全類型表示法。](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html)例如，您可以使用 `UpdateExpressions` 來增加值，而無需先從 DynamoDB 讀取項目，或將個別成員新增至清單。更新表達式目前可在 `updateItem()`方法的自訂擴充功能中使用。

如需使用更新表達式的範例，請參閱本指南中的[自訂延伸模組範例](ddb-en-client-extensions-custom.md)。

如需更新表達式的詳細資訊，請參閱《[Amazon DynamoDB 開發人員指南](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html)》。

# 使用分頁結果：掃描和查詢
<a name="ddb-en-client-use-multirecord"></a>

DynamoDB 增強型用戶端 API 的 `scan``query`和 `batch`方法會傳回具有一或多個*頁面*的回應。頁面包含一或多個項目。您的程式碼可以根據每一頁處理回應，也可以處理個別項目。

同步`DynamoDbEnhancedClient`用戶端傳回的分頁回應會傳回 [PageIterable](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/PageIterable.html) 物件，而非同步傳回的回應則會`DynamoDbEnhancedAsyncClient`傳回 [PagePublisher](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/PagePublisher.html) 物件。

本節會查看處理分頁結果，並提供使用掃描和查詢 APIs的範例。

## 掃描資料表
<a name="ddb-en-client-use-multirecord-scan"></a>

SDK 的 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbAsyncTable.html#scan(java.util.function.Consumer)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbAsyncTable.html#scan(java.util.function.Consumer))方法對應至相同名稱的 [DynamoDB 操作](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Scan.html)。DynamoDB 增強型用戶端 API 提供相同的選項，但它使用熟悉的物件模型並為您處理分頁。

首先，我們透過查看同步映射類別 [DynamoDbTable](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbTable.html) `scan`的方法來探索`PageIterable`界面。

### 使用同步 API
<a name="ddb-en-client-use-multirecord-scan-sync"></a>

下列範例顯示使用 [表達](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/Expression.html)式篩選傳回項目`scan`的方法。[ProductCatalog](ddb-en-client-use.md#ddb-en-client-use-compare-cs3) 是先前顯示的模型物件。

註解行 2 之後顯示的篩選表達式會將傳回`ProductCatalog`的項目限制為價格值介於 8.00 到 80.00 的項目。

此範例也會使用註解行 1 之後顯示`attributesToProject`的方法排除`isbn`值。

在註解行 3 之後， `PageIterable` 物件 `pagedResults`會由 `scan`方法傳回。的 `stream`方法會`PageIterable`傳回[https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html)物件，您可以用來處理頁面。在此範例中，會計算並記錄頁數。

從註解行 4 開始，範例顯示存取`ProductCatalog`項目的兩種變化。註解行 4a 後的版本會串流瀏覽每個頁面，並排序和記錄每個頁面上的項目。註解行 4b 後的版本會略過頁面反覆運算，並直接存取項目。

由於 和 兩個父介面[https://docs.oracle.com/javase/8/docs/api/java/lang/Iterable.html](https://docs.oracle.com/javase/8/docs/api/java/lang/Iterable.html)，`PageIterable`介面提供多種處理結果的方法[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/pagination/sync/SdkIterable.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/pagination/sync/SdkIterable.html)。 `Iterable`帶來 `forEach`、 `iterator`和 `spliterator`方法， `SdkIterable`帶來 `stream`方法。

```
    public static void scanSync(DynamoDbTable<ProductCatalog> productCatalog) {

        Map<String, AttributeValue> expressionValues = Map.of(
                ":min_value", numberValue(8.00),
                ":max_value", numberValue(80.00));

        ScanEnhancedRequest request = ScanEnhancedRequest.builder()
                .consistentRead(true)
                // 1. the 'attributesToProject()' method allows you to specify which values you want returned.
                .attributesToProject("id", "title", "authors", "price")
                // 2. Filter expression limits the items returned that match the provided criteria.
                .filterExpression(Expression.builder()
                        .expression("price >= :min_value AND price <= :max_value")
                        .expressionValues(expressionValues)
                        .build())
                .build();

        // 3. A PageIterable object is returned by the scan method.
        PageIterable<ProductCatalog> pagedResults = productCatalog.scan(request);
        logger.info("page count: {}", pagedResults.stream().count());

        // 4. Log the returned ProductCatalog items using two variations.
        // 4a. This version sorts and logs the items of each page.
        pagedResults.stream().forEach(p -> p.items().stream()
                .sorted(Comparator.comparing(ProductCatalog::price))
                .forEach(
                        item -> logger.info(item.toString())
                ));
        // 4b. This version sorts and logs all items for all pages.
        pagedResults.items().stream()
                .sorted(Comparator.comparing(ProductCatalog::price))
                .forEach(
                        item -> logger.info(item.toString())
                );
    }
```

### 使用非同步 API
<a name="ddb-en-client-use-multirecord-scan-async"></a>

非同步`scan`方法會將結果傳回為`PagePublisher`物件。`PagePublisher` 界面有兩種`subscribe`方法來處理回應頁面。一種`subscribe`方法來自`org.reactivestreams.Publisher`父界面。若要使用此第一個選項處理頁面，請將`[Subscriber](https://www.reactive-streams.org/reactive-streams-1.0.0-javadoc/org/reactivestreams/Subscriber.html)`執行個體傳遞至 `subscribe`方法。以下的第一個範例顯示 `subscribe`方法的使用方式。

第二個`subscribe`方法來自 [SdkPublisher](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/SdkPublisher.html) 界面。此版本的 `subscribe`接受 [https://docs.oracle.com/javase/8/docs/api/java/util/function/Consumer.html](https://docs.oracle.com/javase/8/docs/api/java/util/function/Consumer.html)而非 `Subscriber`。此`subscribe`方法變化會顯示在以下第二個範例中。

下列範例顯示使用上一個範例中所示相同篩選條件表達式的 `scan`方法的非同步版本。

在註解行 3 之後， `DynamoDbAsyncTable.scan` 會傳回`PagePublisher`物件。在下一行，程式碼會建立`org.reactivestreams.Subscriber`界面的執行個體 `ProductCatalogSubscriber`，其會在註解行 4 `PagePublisher`之後訂閱 。

`Subscriber` 物件會在`ProductCatalogSubscriber`類別範例中的註解行 8 之後，收集`onNext`方法中每個頁面`ProductCatalog`的項目。這些項目存放在私有`List`變數中，並使用 `ProductCatalogSubscriber.getSubscribedItems()`方法在呼叫程式碼中存取。這會在註解行 5 之後呼叫。

擷取清單之後，程式碼會依價格排序所有`ProductCatalog`項目，並記錄每個項目。

`ProductCatalogSubscriber` 類別中的 [CountDownLatch](https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CountDownLatch.html) 會封鎖呼叫執行緒，直到所有項目都新增至清單，然後在註解行 5 之後繼續。

```
    public static void scanAsync(DynamoDbAsyncTable productCatalog) {
        ScanEnhancedRequest request = ScanEnhancedRequest.builder()
                .consistentRead(true)
                .attributesToProject("id", "title", "authors", "price")
                .filterExpression(Expression.builder()
                        // 1. :min_value and :max_value are placeholders for the values provided by the map
                        .expression("price >= :min_value AND price <= :max_value")
                        // 2. Two values are needed for the expression and each is supplied as a map entry.
                        .expressionValues(
                                Map.of( ":min_value", numberValue(8.00),
                                        ":max_value", numberValue(400_000.00)))
                        .build())
                .build();

        // 3. A PagePublisher object is returned by the scan method.
        PagePublisher<ProductCatalog> pagePublisher = productCatalog.scan(request);
        ProductCatalogSubscriber subscriber = new ProductCatalogSubscriber();
        // 4. Subscribe the ProductCatalogSubscriber to the PagePublisher.
        pagePublisher.subscribe(subscriber);
        // 5. Retrieve all collected ProductCatalog items accumulated by the subscriber.
        subscriber.getSubscribedItems().stream()
                .sorted(Comparator.comparing(ProductCatalog::price))
                .forEach(item ->
                        logger.info(item.toString()));
        // 6. Use a Consumer to work through each page.
        pagePublisher.subscribe(page -> page
                        .items().stream()
                        .sorted(Comparator.comparing(ProductCatalog::price))
                        .forEach(item ->
                                logger.info(item.toString())))
                .join(); // If needed, blocks the subscribe() method thread until it is finished processing.
        // 7. Use a Consumer to work through each ProductCatalog item.
        pagePublisher.items()
                .subscribe(product -> logger.info(product.toString()))
                .exceptionally(failure -> {
                    logger.error("ERROR  - ", failure);
                    return null;
                })
                .join(); // If needed, blocks the subscribe() method thread until it is finished processing.
    }
```

```
    private static class ProductCatalogSubscriber implements Subscriber<Page<ProductCatalog>> {
        private CountDownLatch latch = new CountDownLatch(1);
        private Subscription subscription;
        private List<ProductCatalog> itemsFromAllPages = new ArrayList<>();

        @Override
        public void onSubscribe(Subscription sub) {
            subscription = sub;
            subscription.request(1L);
            try {
                latch.await(); // Called by main thread blocking it until latch is released.
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

        @Override
        public void onNext(Page<ProductCatalog> productCatalogPage) {
            // 8. Collect all the ProductCatalog instances in the page, then ask the publisher for one more page.
            itemsFromAllPages.addAll(productCatalogPage.items());
            subscription.request(1L);
        }

        @Override
        public void onError(Throwable throwable) {
        }

        @Override
        public void onComplete() {
            latch.countDown(); // Call by subscription thread; latch releases.
        }

        List<ProductCatalog> getSubscribedItems() {
            return this.itemsFromAllPages;
        }
    }
```

下列程式碼片段範例使用在註解行 6 `Consumer`之後接受 的 `PagePublisher.subscribe`方法版本。Java lambda 參數會使用頁面，進一步處理每個項目。在此範例中，會處理每個頁面，然後對每個頁面上的項目進行排序和記錄。

```
        // 6. Use a Consumer to work through each page.
        pagePublisher.subscribe(page -> page
                        .items().stream()
                        .sorted(Comparator.comparing(ProductCatalog::price))
                        .forEach(item ->
                                logger.info(item.toString())))
                .join(); // If needed, blocks the subscribe() method thread until it is finished processing.
```

的 `items`方法會`PagePublisher`取消包裝模型執行個體，讓您的程式碼可以直接處理項目。此方法會顯示在下列程式碼片段中。

```
        // 7. Use a Consumer to work through each ProductCatalog item.
        pagePublisher.items()
                .subscribe(product -> logger.info(product.toString()))
                .exceptionally(failure -> {
                    logger.error("ERROR  - ", failure);
                    return null;
                })
                .join(); // If needed, blocks the subscribe() method thread until it is finished processing.
```

## 查詢資料表
<a name="ddb-en-client-use-multirecord-query"></a>

您可以使用 DynamoDB 增強型用戶端來查詢資料表，並擷取符合特定條件的多個項目。[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbTable.html#query(software.amazon.awssdk.enhanced.dynamodb.model.QueryEnhancedRequest)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbTable.html#query(software.amazon.awssdk.enhanced.dynamodb.model.QueryEnhancedRequest)) 方法會根據主索引鍵值，使用資料類別上定義的 `@DynamoDbPartitionKey`和選用`@DynamoDbSortKey`註釋來尋找項目。

`query()` 方法需要分割區索引鍵值，並選擇性地接受排序索引鍵條件，以進一步精簡結果。如同 `scan` API，查詢會`PageIterable`針對同步呼叫傳回 ，`PagePublisher`針對非同步呼叫傳回 。

### `Query` 方法範例
<a name="ddb-en-client-use-multirecord-query-example"></a>

以下`query()`的方法程式碼範例使用 `MovieActor`類別。資料類別會定義複合主索引鍵，該索引鍵由 **`movie`** 屬性做為分割區索引鍵，而**`actor`**屬性做為排序索引鍵。

#### `MovieActor` 類別
<a name="ddb-en-client-use-movieactor-class"></a>

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

import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbAttribute;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean;
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.DynamoDbSecondarySortKey;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSortKey;

import java.util.Objects;

@DynamoDbBean
public class MovieActor implements Comparable<MovieActor> {

    private String movieName;
    private String actorName;
    private String actingAward;
    private Integer actingYear;
    private String actingSchoolName;

    @DynamoDbPartitionKey
    @DynamoDbAttribute("movie")
    public String getMovieName() {
        return movieName;
    }

    public void setMovieName(String movieName) {
        this.movieName = movieName;
    }

    @DynamoDbSortKey
    @DynamoDbAttribute("actor")
    public String getActorName() {
        return actorName;
    }

    public void setActorName(String actorName) {
        this.actorName = actorName;
    }

    @DynamoDbSecondaryPartitionKey(indexNames = "acting_award_year")
    @DynamoDbAttribute("actingaward")
    public String getActingAward() {
        return actingAward;
    }

    public void setActingAward(String actingAward) {
        this.actingAward = actingAward;
    }

    @DynamoDbSecondarySortKey(indexNames = {"acting_award_year", "movie_year"})
    @DynamoDbAttribute("actingyear")
    public Integer getActingYear() {
        return actingYear;
    }

    public void setActingYear(Integer actingYear) {
        this.actingYear = actingYear;
    }

    @DynamoDbAttribute("actingschoolname")
    public String getActingSchoolName() {
        return actingSchoolName;
    }

    public void setActingSchoolName(String actingSchoolName) {
        this.actingSchoolName = actingSchoolName;
    }

    @Override
    public String toString() {
        final StringBuffer sb = new StringBuffer("MovieActor{");
        sb.append("movieName='").append(movieName).append('\'');
        sb.append(", actorName='").append(actorName).append('\'');
        sb.append(", actingAward='").append(actingAward).append('\'');
        sb.append(", actingYear=").append(actingYear);
        sb.append(", actingSchoolName='").append(actingSchoolName).append('\'');
        sb.append('}');
        return sb.toString();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        MovieActor that = (MovieActor) o;
        return Objects.equals(movieName, that.movieName) && Objects.equals(actorName, that.actorName) && Objects.equals(actingAward, that.actingAward) && Objects.equals(actingYear, that.actingYear) && Objects.equals(actingSchoolName, that.actingSchoolName);
    }

    @Override
    public int hashCode() {
        return Objects.hash(movieName, actorName, actingAward, actingYear, actingSchoolName);
    }

    @Override
    public int compareTo(MovieActor o) {
        if (this.movieName.compareTo(o.movieName) != 0){
            return this.movieName.compareTo(o.movieName);
        } else {
            return this.actorName.compareTo(o.actorName);
        }
    }
}
```

針對下列項目查詢之後的程式碼範例。

#### `MovieActor` 資料表中的項目
<a name="ddb-en-client-use-movieactor-items"></a>

```
MovieActor{movieName='movie01', actorName='actor0', actingAward='actingaward0', actingYear=2001, actingSchoolName='null'}
MovieActor{movieName='movie01', actorName='actor1', actingAward='actingaward1', actingYear=2001, actingSchoolName='actingschool1'}
MovieActor{movieName='movie01', actorName='actor2', actingAward='actingaward2', actingYear=2001, actingSchoolName='actingschool2'}
MovieActor{movieName='movie01', actorName='actor3', actingAward='actingaward3', actingYear=2001, actingSchoolName='null'}
MovieActor{movieName='movie01', actorName='actor4', actingAward='actingaward4', actingYear=2001, actingSchoolName='actingschool4'}
MovieActor{movieName='movie02', actorName='actor0', actingAward='actingaward0', actingYear=2002, actingSchoolName='null'}
MovieActor{movieName='movie02', actorName='actor1', actingAward='actingaward1', actingYear=2002, actingSchoolName='actingschool1'}
MovieActor{movieName='movie02', actorName='actor2', actingAward='actingaward2', actingYear=2002, actingSchoolName='actingschool2'}
MovieActor{movieName='movie02', actorName='actor3', actingAward='actingaward3', actingYear=2002, actingSchoolName='null'}
MovieActor{movieName='movie02', actorName='actor4', actingAward='actingaward4', actingYear=2002, actingSchoolName='actingschool4'}
MovieActor{movieName='movie03', actorName='actor0', actingAward='actingaward0', actingYear=2003, actingSchoolName='null'}
MovieActor{movieName='movie03', actorName='actor1', actingAward='actingaward1', actingYear=2003, actingSchoolName='actingschool1'}
MovieActor{movieName='movie03', actorName='actor2', actingAward='actingaward2', actingYear=2003, actingSchoolName='actingschool2'}
MovieActor{movieName='movie03', actorName='actor3', actingAward='actingaward3', actingYear=2003, actingSchoolName='null'}
MovieActor{movieName='movie03', actorName='actor4', actingAward='actingaward4', actingYear=2003, actingSchoolName='actingschool4'}
```

下列程式碼定義兩個`QueryConditional`執行個體： `keyEqual`（在註解行 1 之後） 和 `sortGreaterThanOrEqualTo`（在註解行 1a 之後）。

#### 依分割區索引鍵查詢項目
<a name="keyEqual-query-conditional-example"></a>

`keyEqual` 執行個體會比對分割區索引鍵值為 的項目**`movie01`**。

此範例也會在註解行 2 之後定義篩選條件表達式，以篩選掉任何沒有 **`actingschoolname`**值的項目。

`QueryEnhancedRequest` 結合了查詢的索引鍵條件和篩選條件表達式。

```
    public static void query(DynamoDbTable movieActorTable) {

        // 1. Define a QueryConditional instance to return items matching a partition value.
        QueryConditional keyEqual = QueryConditional.keyEqualTo(b -> b.partitionValue("movie01"));
        // 1a. Define a QueryConditional that adds a sort key criteria to the partition value criteria.
        QueryConditional sortGreaterThanOrEqualTo = QueryConditional.sortGreaterThanOrEqualTo(b -> b.partitionValue("movie01").sortValue("actor2"));
        // 2. Define a filter expression that filters out items whose attribute value is null.
        final Expression filterOutNoActingschoolname = Expression.builder().expression("attribute_exists(actingschoolname)").build();

        // 3. Build the query request.
        QueryEnhancedRequest tableQuery = QueryEnhancedRequest.builder()
                .queryConditional(keyEqual)
                .filterExpression(filterOutNoActingschoolname)
                .build();
        // 4. Perform the query using the "keyEqual" conditional and filter expression.
        PageIterable<MovieActor> pagedResults = movieActorTable.query(tableQuery);
        logger.info("page count: {}", pagedResults.stream().count()); // Log  number of pages.

        pagedResults.items().stream()
                .sorted()
                .forEach(
                        item -> logger.info(item.toString()) // Log the sorted list of items.
                );
```

**Example – 使用`keyEqual`查詢條件式輸出**  
以下是執行 方法的輸出。輸出會顯示`movieName`值為 **movie01** 的項目，且不會顯示`actingSchoolName`等於 的項目**`null`**。  

```
2023-03-05 13:11:05 [main] INFO  org.example.tests.QueryDemo:46 - page count: 1
2023-03-05 13:11:05 [main] INFO  org.example.tests.QueryDemo:51 - MovieActor{movieName='movie01', actorName='actor1', actingAward='actingaward1', actingYear=2001, actingSchoolName='actingschool1'}
2023-03-05 13:11:05 [main] INFO  org.example.tests.QueryDemo:51 - MovieActor{movieName='movie01', actorName='actor2', actingAward='actingaward2', actingYear=2001, actingSchoolName='actingschool2'}
2023-03-05 13:11:05 [main] INFO  org.example.tests.QueryDemo:51 - MovieActor{movieName='movie01', actorName='actor4', actingAward='actingaward4', actingYear=2001, actingSchoolName='actingschool4'}
```

#### 依分割區索引鍵和排序索引鍵查詢項目
<a name="sort-type-query-conditional-example"></a>

透過為大於或等於 **actor2** 的值新增排序索引鍵條件， 會`sortGreaterThanOrEqualTo``QueryConditional`精簡分割區索引鍵比對 (**movie01**)。

開頭為 [`QueryConditional`的方法](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/QueryConditional.html)`sort`需要分割區索引鍵值才能比對，並根據排序索引鍵值進行比較，進一步精簡查詢。在方法名稱`Sort`中， 並不表示結果會排序，但排序索引鍵值將用於比較。

在下列程式碼片段中，我們會變更先前在註解行 3 之後顯示的查詢請求。此程式碼片段會將「keyEqual」查詢條件取代為註解行 1a 之後定義的「sortGreaterThanOrEqualTo」查詢條件。下列程式碼也會移除篩選條件表達式。

```
        QueryEnhancedRequest tableQuery = QueryEnhancedRequest.builder()
                .queryConditional(sortGreaterThanOrEqualTo).build();
```

**Example – 使用`sortGreaterThanOrEqualTo`查詢條件式輸出**  
下列輸出會顯示查詢的結果。查詢會傳回`movieName`值等於 **movie01** 的項目，且只有`actorName`值大於或等於 **actor2** 的項目。由於我們移除篩選條件，查詢會傳回屬性沒有值的項目`actingSchoolName`。  

```
2023-03-05 13:15:00 [main] INFO  org.example.tests.QueryDemo:46 - page count: 1
2023-03-05 13:15:00 [main] INFO  org.example.tests.QueryDemo:51 - MovieActor{movieName='movie01', actorName='actor2', actingAward='actingaward2', actingYear=2001, actingSchoolName='actingschool2'}
2023-03-05 13:15:00 [main] INFO  org.example.tests.QueryDemo:51 - MovieActor{movieName='movie01', actorName='actor3', actingAward='actingaward3', actingYear=2001, actingSchoolName='null'}
2023-03-05 13:15:00 [main] INFO  org.example.tests.QueryDemo:51 - MovieActor{movieName='movie01', actorName='actor4', actingAward='actingaward4', actingYear=2001, actingSchoolName='actingschool4'}
```

# 執行批次操作
<a name="ddb-en-client-use-multiop-batch"></a>

DynamoDB 增強型用戶端 API 提供兩種批次方法：[`batchGetItem`()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html#batchGetItem(java.util.function.Consumer)) 和 [`batchWriteItem`()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html#batchWriteItem(java.util.function.Consumer))。

## `batchGetItem()` 範例
<a name="ddb-en-client-use-multiop-batch-get"></a>

使用 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html#batchGetItem(java.util.function.Consumer)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html#batchGetItem(java.util.function.Consumer))方法，您可以在一個整體請求中跨多個資料表擷取最多 100 個個別項目。下列範例使用先前顯示的 [`Customer`](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-anno-bean-cust)和 [`MovieActor`](ddb-en-client-use-multirecord.md#ddb-en-client-use-movieactor-class) 資料類別。

在第 1 行和第 2 行之後的範例中，您會建置稍後在註解第 3 行之後新增為`batchGetItem()`方法參數的`[ReadBatch](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/ReadBatch.html)`物件。

註解行 1 後的程式碼會建置要從`Customer`資料表讀取的批次。註解行 1a 之後的程式碼顯示使用採用主索引鍵值和排序索引鍵值的`[GetItemEnhancedRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/GetItemEnhancedRequest.Builder.html)`建置器，以指定要讀取的項目。如果資料類別具有複合索引鍵，您必須同時提供分割區索引鍵值和排序索引鍵值。

與指定要請求項目的索引鍵值相反，您可以使用資料類別來請求項目，如註解行 1b 所示。開發套件會在提交請求之前擷取幕後的索引鍵值。

當您使用金鑰型方法指定項目時，如 2a 之後的兩個陳述式所示，您也可以指定 DynamoDB 應執行[高度一致讀取](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ReadConsistency.html)。使用 `consistentRead()`方法時，必須在相同資料表的所有請求項目上使用。

若要擷取 DynamoDB 找到的項目，請使用註解行 4 後顯示`[resultsForTable() ](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/BatchGetResultPage.html#resultsForTable(software.amazon.awssdk.enhanced.dynamodb.MappedTableResource))`的方法。針對請求中讀取的每個資料表呼叫 方法。 會`resultsForTable()`傳回可使用任何`java.util.List`方法處理的找到項目清單。此範例會記錄每個項目。

若要探索 DynamoDB 未處理的項目，請在註解行 5 之後使用 方法。`BatchGetResultPage` 類別具有 `[unprocessedKeysForTable()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/BatchGetResultPage.html#unprocessedKeysForTable(software.amazon.awssdk.enhanced.dynamodb.MappedTableResource))`方法，可讓您存取每個未處理的金鑰。[BatchGetItem API 參考](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchGetItem.html)提供有關導致未處理項目之情況的詳細資訊。

```
    public static void batchGetItemExample(DynamoDbEnhancedClient enhancedClient,
                                           DynamoDbTable<Customer> customerTable,
                                           DynamoDbTable<MovieActor> movieActorTable) {

        Customer customer2 = new Customer();
        customer2.setId("2");
        customer2.setEmail("cust2@example.org");

        // 1. Build a batch to read from the Customer table.
        ReadBatch customerBatch = ReadBatch.builder(Customer.class)
                .mappedTableResource(customerTable)
                // 1a. Specify the primary key value and sort key value for the item.
                .addGetItem(b -> b.key(k -> k.partitionValue("1").sortValue("cust1@orgname.org")))
                // 1b. Alternatively, supply a data class instances to provide the primary key values.
                .addGetItem(customer2)
                .build();

        // 2. Build a batch to read from the MovieActor table.
        ReadBatch moveActorBatch = ReadBatch.builder(MovieActor.class)
                .mappedTableResource(movieActorTable)
                // 2a. Call consistentRead(Boolean.TRUE) for each item for the same table.
                .addGetItem(b -> b.key(k -> k.partitionValue("movie01").sortValue("actor1")).consistentRead(Boolean.TRUE))
                .addGetItem(b -> b.key(k -> k.partitionValue("movie01").sortValue("actor4")).consistentRead(Boolean.TRUE))
                .build();

        // 3. Add ReadBatch objects to the request.
        BatchGetResultPageIterable resultPages = enhancedClient.batchGetItem(b -> b.readBatches(customerBatch, moveActorBatch));

        // 4. Retrieve the successfully requested items from each table.
        resultPages.resultsForTable(customerTable).forEach(item -> logger.info(item.toString()));
        resultPages.resultsForTable(movieActorTable).forEach(item -> logger.info(item.toString()));

        // 5. Retrieve the keys of the items requested but not processed by the service.
        resultPages.forEach((BatchGetResultPage pageResult) -> {
            pageResult.unprocessedKeysForTable(customerTable).forEach(key -> logger.info("Unprocessed item key: " + key.toString()));
            pageResult.unprocessedKeysForTable(movieActorTable).forEach(key -> logger.info("Unprocessed item key: " + key.toString()));
        });
    }
```

在執行範例程式碼之前，假設兩個資料表中有下列項目。

### 資料表中的項目
<a name="ddb-en-client-use-multiop-batch-get-tableitems"></a>

```
Customer [id=1, name=CustName1, email=cust1@example.org, regDate=2023-03-31T15:46:27.688Z]
Customer [id=2, name=CustName2, email=cust2@example.org, regDate=2023-03-31T15:46:28.688Z]
Customer [id=3, name=CustName3, email=cust3@example.org, regDate=2023-03-31T15:46:29.688Z]
Customer [id=4, name=CustName4, email=cust4@example.org, regDate=2023-03-31T15:46:30.688Z]
Customer [id=5, name=CustName5, email=cust5@example.org, regDate=2023-03-31T15:46:31.689Z]
MovieActor{movieName='movie01', actorName='actor0', actingAward='actingaward0', actingYear=2001, actingSchoolName='null'}
MovieActor{movieName='movie01', actorName='actor1', actingAward='actingaward1', actingYear=2001, actingSchoolName='actingschool1'}
MovieActor{movieName='movie01', actorName='actor2', actingAward='actingaward2', actingYear=2001, actingSchoolName='actingschool2'}
MovieActor{movieName='movie01', actorName='actor3', actingAward='actingaward3', actingYear=2001, actingSchoolName='null'}
MovieActor{movieName='movie01', actorName='actor4', actingAward='actingaward4', actingYear=2001, actingSchoolName='actingschool4'}
```

下列輸出顯示註解行 4 之後傳回和記錄的項目。

```
Customer [id=1, name=CustName1, email=cust1@example.org, regDate=2023-03-31T15:46:27.688Z]
Customer [id=2, name=CustName2, email=cust2@example.org, regDate=2023-03-31T15:46:28.688Z]
MovieActor{movieName='movie01', actorName='actor4', actingAward='actingaward4', actingYear=2001, actingSchoolName='actingschool4'}
MovieActor{movieName='movie01', actorName='actor1', actingAward='actingaward1', actingYear=2001, actingSchoolName='actingschool1'}
```

## `batchWriteItem()` 範例
<a name="ddb-en-client-use-multiop-batch-write"></a>

`batchWriteItem()` 方法會在一或多個資料表中放置或刪除多個項目。您可以在請求中指定最多 25 個個別放置或刪除操作。下列範例使用先前顯示的 [`ProductCatalog`](ddb-en-client-use.md#ddb-en-client-use-compare-cs3)和 [`MovieActor`](ddb-en-client-use-multirecord.md#ddb-en-client-use-movieactor-class) 模型類別。

`WriteBatch` 物件是在註解行 1 和 2 之後建置。對於`ProductCatalog`資料表，程式碼會放置一個項目並刪除一個項目。對於註解行 2 之後的`MovieActor`資料表，程式碼會放置兩個項目並刪除一個項目。

`batchWriteItem` 方法會在註解行 3 之後呼叫。`[builder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/BatchWriteItemEnhancedRequest.Builder.html)` 參數提供每個資料表的批次請求。

傳回的`[BatchWriteResult](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/BatchWriteResult.html)`物件為每個操作提供不同的方法來檢視未處理的請求。註解行 4a 後的程式碼提供未處理的刪除請求金鑰，而註解行 4b 後的程式碼提供未處理的放置項目。

```
    public static void batchWriteItemExample(DynamoDbEnhancedClient enhancedClient,
                                             DynamoDbTable<ProductCatalog> catalogTable,
                                             DynamoDbTable<MovieActor> movieActorTable) {

        // 1. Build a batch to write to the ProductCatalog table.
        WriteBatch products = WriteBatch.builder(ProductCatalog.class)
                .mappedTableResource(catalogTable)
                .addPutItem(b -> b.item(getProductCatItem1()))
                .addDeleteItem(b -> b.key(k -> k
                        .partitionValue(getProductCatItem2().id())
                        .sortValue(getProductCatItem2().title())))
                .build();

        // 2. Build a batch to write to the MovieActor table.
        WriteBatch movies = WriteBatch.builder(MovieActor.class)
                .mappedTableResource(movieActorTable)
                .addPutItem(getMovieActorYeoh())
                .addPutItem(getMovieActorBlanchettPartial())
                .addDeleteItem(b -> b.key(k -> k
                        .partitionValue(getMovieActorStreep().getMovieName())
                        .sortValue(getMovieActorStreep().getActorName())))
                .build();

        // 3. Add WriteBatch objects to the request.
        BatchWriteResult batchWriteResult = enhancedClient.batchWriteItem(b -> b.writeBatches(products, movies));
        // 4. Retrieve keys for items the service did not process.
        // 4a. 'unprocessedDeleteItemsForTable()' returns keys for delete requests that did not process.
        if (batchWriteResult.unprocessedDeleteItemsForTable(movieActorTable).size() > 0) {
            batchWriteResult.unprocessedDeleteItemsForTable(movieActorTable).forEach(key ->
                    logger.info(key.toString()));
        }
        // 4b. 'unprocessedPutItemsForTable()' returns keys for put requests that did not process.
        if (batchWriteResult.unprocessedPutItemsForTable(catalogTable).size() > 0) {
            batchWriteResult.unprocessedPutItemsForTable(catalogTable).forEach(key ->
                    logger.info(key.toString()));
        }
    }
```

下列協助程式方法提供放置和刪除操作的模型物件。

### 協助程式方法
<a name="ddb-en-client-use-multiop-batch-write-helpers"></a>

```
 1.     public static ProductCatalog getProductCatItem1() {
 2.         return ProductCatalog.builder()
 3.                 .id(2)
 4.                 .isbn("1-565-85698")
 5.                 .authors(new HashSet<>(Arrays.asList("a", "b")))
 6.                 .price(BigDecimal.valueOf(30.22))
 7.                 .title("Title 55")
 8.                 .build();
 9.     }
10. 
11.     public static ProductCatalog getProductCatItem2() {
12.         return ProductCatalog.builder()
13.                 .id(4)
14.                 .price(BigDecimal.valueOf(40.00))
15.                 .title("Title 1")
16.                 .build();
17.     }  
18. 
19.     public static MovieActor getMovieActorBlanchettPartial() {
20.         MovieActor movieActor = new MovieActor();
21.         movieActor.setActorName("Cate Blanchett");
22.         movieActor.setMovieName("Blue Jasmine");
23.         movieActor.setActingYear(2023);
24.         movieActor.setActingAward("Best Actress");
25.         return movieActor;
26.     }
27. 
28.     public static MovieActor getMovieActorStreep() {
29.         MovieActor movieActor = new MovieActor();
30.         movieActor.setActorName("Meryl Streep");
31.         movieActor.setMovieName("Sophie's Choice");
32.         movieActor.setActingYear(1982);
33.         movieActor.setActingAward("Best Actress");
34.         movieActor.setActingSchoolName("Yale School of Drama");
35.         return movieActor;
36.     }
37. 
38.     public static MovieActor getMovieActorYeoh(){
39.         MovieActor movieActor = new MovieActor();
40.         movieActor.setActorName("Michelle Yeoh");
41.         movieActor.setMovieName("Everything Everywhere All at Once");
42.         movieActor.setActingYear(2023);
43.         movieActor.setActingAward("Best Actress");
44.         movieActor.setActingSchoolName("Royal Academy of Dance");
45.         return movieActor;
46.     }
```

在執行範例程式碼之前，假設資料表包含下列項目。

```
MovieActor{movieName='Blue Jasmine', actorName='Cate Blanchett', actingAward='Best Actress', actingYear=2013, actingSchoolName='National Institute of Dramatic Art'}
MovieActor{movieName='Sophie's Choice', actorName='Meryl Streep', actingAward='Best Actress', actingYear=1982, actingSchoolName='Yale School of Drama'}
ProductCatalog{id=4, title='Title 1', isbn='orig_isbn', authors=[b, g], price=10}
```

範例程式碼完成後，資料表會包含下列項目。

```
MovieActor{movieName='Blue Jasmine', actorName='Cate Blanchett', actingAward='Best Actress', actingYear=2013, actingSchoolName='null'}
MovieActor{movieName='Everything Everywhere All at Once', actorName='Michelle Yeoh', actingAward='Best Actress', actingYear=2023, actingSchoolName='Royal Academy of Dance'}
ProductCatalog{id=2, title='Title 55', isbn='1-565-85698', authors=[a, b], price=30.22}
```

請注意，`MovieActor`資料表中的`Blue Jasmine`電影項目已取代為透過`getMovieActorBlanchettPartial()`協助程式方法取得之放置請求中使用的項目。如果未提供資料 Bean 屬性值，則會移除資料庫中的值。這就是`Blue Jasmine`電影項目產生的 `actingSchoolName` 為 null 的原因。

**注意**  
雖然 API 文件指出可以使用條件表達式，而且可以使用個別[的放置](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/PutItemEnhancedRequest.html)和[刪除](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/DeleteItemEnhancedRequest.html)請求傳回耗用容量和集合指標，但這不是批次寫入案例的情況。為了改善批次操作的效能，會忽略這些個別選項。

# 執行交易操作
<a name="ddb-en-client-use-multiop-trans"></a>

DynamoDB 增強型用戶端 API 提供 `transactGetItems()`和 `transactWriteItems()`方法。適用於 Java 的 SDK 的交易方法在 DynamoDB 資料表中提供原子性、一致性、隔離性和耐久性 (ACID)，協助您在應用程式中維持資料正確性。

## `transactGetItems()` 範例
<a name="ddb-en-client-use-multiop-trans-getitems"></a>

`[transactGetItems()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html#transactGetItems(java.util.function.Consumer))` 方法最多接受 100 個個別的項目請求。所有項目都會在單一原子交易中讀取。*Amazon DynamoDB 開發人員指南*提供有關[導致`transactGetItems()`方法失敗的條件](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/transaction-apis.html#transaction-apis-txgetitems)，以及呼叫 時所用隔離層級的資訊`[transactGetItem()](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/transaction-apis.html#transaction-isolation)`。

在下列範例中的註解行 1 之後，程式碼會使用 `[builder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/TransactGetItemsEnhancedRequest.Builder.html)` 參數呼叫 `transactGetItems()`方法。建置器的 `[addGetItem()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/TransactGetItemsEnhancedRequest.Builder.html#addGetItem(software.amazon.awssdk.enhanced.dynamodb.MappedTableResource,T))`會使用包含 SDK 將用於產生最終請求之金鑰值的資料物件叫用三次。

請求會在註解行 2 之後傳回`[Document](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/Document.html)`物件清單。傳回的文件清單包含與請求順序相同的項目資料非空[的文件](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/Document.html)執行個體。如果傳回項目資料，`[Document.getItem(MappedTableResource<T> mappedTableResource)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/Document.html#getItem(software.amazon.awssdk.enhanced.dynamodb.MappedTableResource))`方法會將未輸入`Document`物件轉換為類型 Java 物件，否則方法會傳回 null。

```
    public static void transactGetItemsExample(DynamoDbEnhancedClient enhancedClient,
                                               DynamoDbTable<ProductCatalog> catalogTable,
                                               DynamoDbTable<MovieActor> movieActorTable) {

        // 1. Request three items from two tables using a builder.
        final List<Document> documents = enhancedClient.transactGetItems(b -> b
                .addGetItem(catalogTable, Key.builder().partitionValue(2).sortValue("Title 55").build())
                .addGetItem(movieActorTable, Key.builder().partitionValue("Sophie's Choice").sortValue("Meryl Streep").build())
                .addGetItem(movieActorTable, Key.builder().partitionValue("Blue Jasmine").sortValue("Cate Blanchett").build())
                .build());

        // 2. A list of Document objects is returned in the same order as requested.
        ProductCatalog title55 = documents.get(0).getItem(catalogTable);
        if (title55 != null) {
            logger.info(title55.toString());
        }

        MovieActor sophiesChoice = documents.get(1).getItem(movieActorTable);
        if (sophiesChoice != null) {
            logger.info(sophiesChoice.toString());
        }

        // 3. The getItem() method returns null if the Document object contains no item from DynamoDB.
        MovieActor blueJasmine = documents.get(2).getItem(movieActorTable);
        if (blueJasmine != null) {
            logger.info(blueJasmine.toString());
        }
    }
```

DynamoDB 資料表在程式碼範例執行之前包含下列項目。

```
ProductCatalog{id=2, title='Title 55', isbn='orig_isbn', authors=[b, g], price=10}
MovieActor{movieName='Sophie's Choice', actorName='Meryl Streep', actingAward='Best Actress', actingYear=1982, actingSchoolName='Yale School of Drama'}
```

系統會記錄下列輸出。如果請求項目但找不到，則不會傳回，因為名為 之電影請求的情況。 `Blue Jasmine`

```
ProductCatalog{id=2, title='Title 55', isbn='orig_isbn', authors=[b, g], price=10}
MovieActor{movieName='Sophie's Choice', actorName='Meryl Streep', actingAward='Best Actress', actingYear=1982, actingSchoolName='Yale School of Drama'}
```

## `transactWriteItems()` 範例
<a name="ddb-en-client-use-multiop-trans-writeitems"></a>

在跨多個資料表的單一原子交易中， `[transactWriteItems()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html#transactWriteItems(java.util.function.Consumer))` 接受最多 100 個放置、更新或刪除動作。*Amazon DynamoDB 開發人員指南*包含[基礎 DynamoDB 服務操作](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/transaction-apis.html#transaction-apis-txwriteitems)之限制和失敗條件的詳細資訊。

### 基本範例
<a name="ddb-en-client-use-multiop-trans-writeitems-basic"></a>

在下列範例中，兩個資料表需要四個操作。對應的模型類別 [`ProductCatalog`](ddb-en-client-use.md#ddb-en-client-use-compare-cs3)和 [`MovieActor`](ddb-en-client-use-multirecord.md#ddb-en-client-use-movieactor-class) 先前已顯示。

三個可能的操作 - 輸入、更新和刪除 - 都使用專用請求參數來指定詳細資訊。

註解行 1 後的程式碼顯示 `addPutItem()`方法的簡單變化。方法接受`[MappedTableResource](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/MappedTableResource.html)`物件和要放置的資料物件執行個體。註解行 2 後的陳述式會顯示接受`[TransactPutItemEnhancedRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/TransactPutItemEnhancedRequest.html)`執行個體的變化。此變化可讓您在請求中新增更多選項，例如條件表達式。後續[範例](#ddb-en-client-use-multiop-trans-writeitems-opcondition)顯示個別操作的條件表達式。

在註解行 3 之後請求更新操作。 `[TransactUpdateItemEnhancedRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/TransactUpdateItemEnhancedRequest.Builder.html)` 具有 `ignoreNulls()`方法，可讓您設定 SDK 對模型物件上的`null`值執行的操作。如果 `ignoreNulls()` 方法傳回 true，則 SDK 不會為 的資料物件屬性移除資料表的屬性值`null`。如果 `ignoreNulls()`方法傳回 false，則 SDK 會請求 DynamoDB 服務從資料表中的項目中移除屬性。的預設值`ignoreNulls`為 false。

註解行 4 後的 陳述式會顯示接受資料物件的刪除請求變化。增強型用戶端會在分派最終請求之前擷取金鑰值。

```
    public static void transactWriteItems(DynamoDbEnhancedClient enhancedClient,
                                          DynamoDbTable<ProductCatalog> catalogTable,
                                          DynamoDbTable<MovieActor> movieActorTable) {

        enhancedClient.transactWriteItems(b -> b
                // 1. Simplest variation of put item request.
                .addPutItem(catalogTable, getProductCatId2())
                // 2. Put item request variation that accommodates condition expressions.
                .addPutItem(movieActorTable, TransactPutItemEnhancedRequest.builder(MovieActor.class)
                        .item(getMovieActorStreep())
                        .conditionExpression(Expression.builder().expression("attribute_not_exists (movie)").build())
                        .build())
                // 3. Update request that does not remove attribute values on the table if the data object's value is null.
                .addUpdateItem(catalogTable, TransactUpdateItemEnhancedRequest.builder(ProductCatalog.class)
                        .item(getProductCatId4ForUpdate())
                        .ignoreNulls(Boolean.TRUE)
                        .build())
                // 4. Variation of delete request that accepts a data object. The key values are extracted for the request.
                .addDeleteItem(movieActorTable, getMovieActorBlanchett())
        );
    }
```

下列協助程式方法提供`add*Item`參數的資料物件。

#### 協助程式方法
<a name="ddb-en-client-use-multiop-trans-writeitems-basic-helpers"></a>

```
    public static ProductCatalog getProductCatId2() {
        return ProductCatalog.builder()
                .id(2)
                .isbn("1-565-85698")
                .authors(new HashSet<>(Arrays.asList("a", "b")))
                .price(BigDecimal.valueOf(30.22))
                .title("Title 55")
                .build();
    }

    public static ProductCatalog getProductCatId4ForUpdate() {
        return ProductCatalog.builder()
                .id(4)
                .price(BigDecimal.valueOf(40.00))
                .title("Title 1")
                .build();
    }

    public static MovieActor getMovieActorBlanchett() {
        MovieActor movieActor = new MovieActor();
        movieActor.setActorName("Cate Blanchett");
        movieActor.setMovieName("Tar");
        movieActor.setActingYear(2022);
        movieActor.setActingAward("Best Actress");
        movieActor.setActingSchoolName("National Institute of Dramatic Art");
        return movieActor;
    }

    public static MovieActor getMovieActorStreep() {
        MovieActor movieActor = new MovieActor();
        movieActor.setActorName("Meryl Streep");
        movieActor.setMovieName("Sophie's Choice");
        movieActor.setActingYear(1982);
        movieActor.setActingAward("Best Actress");
        movieActor.setActingSchoolName("Yale School of Drama");
        return movieActor;
    }
```

DynamoDB 資料表在程式碼範例執行之前包含下列項目。

```
1 | ProductCatalog{id=4, title='Title 1', isbn='orig_isbn', authors=[b, g], price=10}
2 | MovieActor{movieName='Tar', actorName='Cate Blanchett', actingAward='Best Actress', actingYear=2022, actingSchoolName='National Institute of Dramatic Art'}
```

程式碼完成執行後，資料表中會包含下列項目。

```
3 | ProductCatalog{id=2, title='Title 55', isbn='1-565-85698', authors=[a, b], price=30.22}
4 | ProductCatalog{id=4, title='Title 1', isbn='orig_isbn', authors=[b, g], price=40.0}
5 | MovieActor{movieName='Sophie's Choice', actorName='Meryl Streep', actingAward='Best Actress', actingYear=1982, actingSchoolName='Yale School of Drama'}
```

第 2 行的項目已刪除，第 3 行和第 5 行顯示放置的項目。第 4 行顯示第 1 行的更新。此`price`值是項目上唯一變更的值。如果 `ignoreNulls()` 傳回 false，則第 4 行看起來會如下行所示。

```
ProductCatalog{id=4, title='Title 1', isbn='null', authors=null, price=40.0}
```

### 條件檢查範例
<a name="ddb-en-client-use-multiop-trans-writeitems-checkcond"></a>

下列範例顯示使用條件檢查。條件檢查用於檢查項目是否存在，或檢查資料庫中項目特定屬性的條件。在條件檢查中檢查的項目無法在交易中的另一個操作中使用。

**注意**  
在同一筆交易中，您無法針對同一個項目進行多項操作。例如，您無法執行條件檢查，也無法嘗試更新相同交易中的相同項目。

此範例顯示交易寫入項目請求中的每種操作類型之一。在註解行 2 之後，如果 `conditionExpression` 參數評估為 ，則 `addConditionCheck()`方法會提供交易失敗的條件`false`。從協助程式方法區塊中顯示的方法傳回的條件表達式會檢查電影的獎勵年份是否`Sophie's Choice`不等於 `1982`。如果是，表達式會評估為 ，`false`且交易會失敗。

本指南在另一個主題中深入討論[表達式](ddb-en-client-expressions.md)。

```
    public static void conditionCheckFailExample(DynamoDbEnhancedClient enhancedClient,
                                                 DynamoDbTable<ProductCatalog> catalogTable,
                                                 DynamoDbTable<MovieActor> movieActorTable) {

        try {
            enhancedClient.transactWriteItems(b -> b
                    // 1. Perform one of each type of operation with the next three methods.
                    .addPutItem(catalogTable, TransactPutItemEnhancedRequest.builder(ProductCatalog.class)
                            .item(getProductCatId2()).build())
                    .addUpdateItem(catalogTable, TransactUpdateItemEnhancedRequest.builder(ProductCatalog.class)
                            .item(getProductCatId4ForUpdate())
                            .ignoreNulls(Boolean.TRUE).build())
                    .addDeleteItem(movieActorTable, TransactDeleteItemEnhancedRequest.builder()
                            .key(b1 -> b1
                                    .partitionValue(getMovieActorBlanchett().getMovieName())
                                    .sortValue(getMovieActorBlanchett().getActorName())).build())
                    // 2. Add a condition check on a table item that is not involved in another operation in this request.
                    .addConditionCheck(movieActorTable, ConditionCheck.builder()
                            .conditionExpression(buildConditionCheckExpression())
                            .key(k -> k
                                    .partitionValue("Sophie's Choice")
                                    .sortValue("Meryl Streep"))
                            // 3. Specify the request to return existing values from the item if the condition evaluates to true.
                            .returnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure.ALL_OLD)
                            .build())
                    .build());
        // 4. Catch the exception if the transaction fails and log the information.
        } catch (TransactionCanceledException ex) {
            ex.cancellationReasons().stream().forEach(cancellationReason -> {
                logger.info(cancellationReason.toString());
            });
        }
    }
```

下列協助程式方法用於先前的程式碼範例。

#### 協助程式方法
<a name="ddb-en-client-use-multiop-trans-writeitems-checkcond-helpers"></a>

```
    private static Expression buildConditionCheckExpression() {
        Map<String, AttributeValue> expressionValue = Map.of(
                ":year", numberValue(1982));

        return Expression.builder()
                .expression("actingyear <> :year")
                .expressionValues(expressionValue)
                .build();
    }

    public static ProductCatalog getProductCatId2() {
        return ProductCatalog.builder()
                .id(2)
                .isbn("1-565-85698")
                .authors(new HashSet<>(Arrays.asList("a", "b")))
                .price(BigDecimal.valueOf(30.22))
                .title("Title 55")
                .build();
    }

    public static ProductCatalog getProductCatId4ForUpdate() {
        return ProductCatalog.builder()
                .id(4)
                .price(BigDecimal.valueOf(40.00))
                .title("Title 1")
                .build();
    }

    public static MovieActor getMovieActorBlanchett() {
        MovieActor movieActor = new MovieActor();
        movieActor.setActorName("Cate Blanchett");
        movieActor.setMovieName("Blue Jasmine");
        movieActor.setActingYear(2013);
        movieActor.setActingAward("Best Actress");
        movieActor.setActingSchoolName("National Institute of Dramatic Art");
        return movieActor;
    }
```

DynamoDB 資料表在程式碼範例執行之前包含下列項目。

```
1 | ProductCatalog{id=4, title='Title 1', isbn='orig_isbn', authors=[b, g], price=10}
2 | MovieActor{movieName='Sophie's Choice', actorName='Meryl Streep', actingAward='Best Actress', actingYear=1982, actingSchoolName='Yale School of Drama'}
3 | MovieActor{movieName='Tar', actorName='Cate Blanchett', actingAward='Best Actress', actingYear=2022, actingSchoolName='National Institute of Dramatic Art'}
```

程式碼完成執行後，資料表中會包含下列項目。

```
ProductCatalog{id=4, title='Title 1', isbn='orig_isbn', authors=[b, g], price=10}
MovieActor{movieName='Sophie's Choice', actorName='Meryl Streep', actingAward='Best Actress', actingYear=1982, actingSchoolName='Yale School of Drama'}
MovieActor{movieName='Tar', actorName='Cate Blanchett', actingAward='Best Actress', actingYear=2022, actingSchoolName='National Institute of Dramatic Art'}
```

因為交易失敗，所以項目在資料表中保持不變。電影`actingYear`的值`Sophie's Choice`為 `1982`，如呼叫 `transactWriteItem()` 方法之前資料表中項目的第 2 行所示。

若要擷取交易的取消資訊，請將`transactWriteItems()`方法呼叫括在 `try` 區塊和 `catch` 中[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/TransactionCanceledException.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/TransactionCanceledException.html)。在範例的註解行 4 之後，程式碼會記錄每個`[CancellationReason](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/CancellationReason.html)`物件。由於範例註解行 3 後面的程式碼指定應針對導致交易失敗的項目傳回值，因此日誌會顯示`Sophie's Choice`電影項目的原始資料庫值。

```
CancellationReason(Code=None)
CancellationReason(Code=None)
CancellationReason(Code=None)
CancellationReason(Item={actor=AttributeValue(S=Meryl Streep), movie=AttributeValue(S=Sophie's Choice), actingaward=AttributeValue(S=Best Actress), actingyear=AttributeValue(N=1982), actingschoolname=AttributeValue(S=Yale School of Drama)}, ¬
    Code=ConditionalCheckFailed, Message=The conditional request failed.)
```

### 單一操作條件範例
<a name="ddb-en-client-use-multiop-trans-writeitems-opcondition"></a>

下列範例顯示對交易請求中的單一操作使用條件。註解行 1 後的刪除操作包含一個條件，可針對資料庫檢查操作目標項目的值。在此範例中，註解行 2 之後使用協助程式方法建立的條件表達式指定，如果電影的動作年份不等於 2013 年，則應從資料庫中刪除項目。

本指南稍後會討論[表達式](ddb-en-client-expressions.md)。

```
    public static void singleOperationConditionFailExample(DynamoDbEnhancedClient enhancedClient,
                                                           DynamoDbTable<ProductCatalog> catalogTable,
                                                           DynamoDbTable<MovieActor> movieActorTable) {
        try {
            enhancedClient.transactWriteItems(b -> b
                    .addPutItem(catalogTable, TransactPutItemEnhancedRequest.builder(ProductCatalog.class)
                            .item(getProductCatId2())
                            .build())
                    .addUpdateItem(catalogTable, TransactUpdateItemEnhancedRequest.builder(ProductCatalog.class)
                            .item(getProductCatId4ForUpdate())
                            .ignoreNulls(Boolean.TRUE).build())
                    // 1. Delete operation that contains a condition expression
                    .addDeleteItem(movieActorTable, TransactDeleteItemEnhancedRequest.builder()
                            .key((Key.Builder k) -> {
                                MovieActor blanchett = getMovieActorBlanchett();
                                k.partitionValue(blanchett.getMovieName())
                                        .sortValue(blanchett.getActorName());
                            })
                            .conditionExpression(buildDeleteItemExpression())
                            .returnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure.ALL_OLD)
                            .build())
                    .build());
        } catch (TransactionCanceledException ex) {
            ex.cancellationReasons().forEach(cancellationReason -> logger.info(cancellationReason.toString()));
        }
    }

    // 2. Provide condition expression to check if 'actingyear' is not equal to 2013.
    private static Expression buildDeleteItemExpression() {
        Map<String, AttributeValue> expressionValue = Map.of(
                ":year", numberValue(2013));

        return Expression.builder()
                .expression("actingyear <> :year")
                .expressionValues(expressionValue)
                .build();
    }
```

下列協助程式方法用於先前的程式碼範例。

#### 協助程式方法
<a name="ddb-en-client-use-multiop-trans-writeitems-opcondition-helpers"></a>

```
    public static ProductCatalog getProductCatId2() {
        return ProductCatalog.builder()
                .id(2)
                .isbn("1-565-85698")
                .authors(new HashSet<>(Arrays.asList("a", "b")))
                .price(BigDecimal.valueOf(30.22))
                .title("Title 55")
                .build();
    }

    public static ProductCatalog getProductCatId4ForUpdate() {
        return ProductCatalog.builder()
                .id(4)
                .price(BigDecimal.valueOf(40.00))
                .title("Title 1")
                .build();
    }
    public static MovieActor getMovieActorBlanchett() {
        MovieActor movieActor = new MovieActor();
        movieActor.setActorName("Cate Blanchett");
        movieActor.setMovieName("Blue Jasmine");
        movieActor.setActingYear(2013);
        movieActor.setActingAward("Best Actress");
        movieActor.setActingSchoolName("National Institute of Dramatic Art");
        return movieActor;
    }
```

DynamoDB 資料表在程式碼範例執行之前包含下列項目。

```
1 | ProductCatalog{id=4, title='Title 1', isbn='orig_isbn', authors=[b, g], price=10}
2 | MovieActor{movieName='Blue Jasmine', actorName='Cate Blanchett', actingAward='Best Actress', actingYear=2013, actingSchoolName='National Institute of Dramatic Art'}
```

程式碼完成執行後，資料表中會包含下列項目。

```
ProductCatalog{id=4, title='Title 1', isbn='orig_isbn', authors=[b, g], price=10}
2023-03-15 11:29:07 [main] INFO  org.example.tests.TransactDemoTest:168 - MovieActor{movieName='Blue Jasmine', actorName='Cate Blanchett', actingAward='Best Actress', actingYear=2013, actingSchoolName='National Institute of Dramatic Art'}
```

因為交易失敗，所以項目在資料表中保持不變。電影`actingYear`的值`Blue Jasmine``2013`如程式碼範例執行前項目清單中的第 2 行所示。

以下幾行會記錄到 主控台。

```
CancellationReason(Code=None)
CancellationReason(Code=None)
CancellationReason(Item={actor=AttributeValue(S=Cate Blanchett), movie=AttributeValue(S=Blue Jasmine), actingaward=AttributeValue(S=Best Actress), actingyear=AttributeValue(N=2013), actingschoolname=AttributeValue(S=National Institute of Dramatic Art)}, 
    Code=ConditionalCheckFailed, Message=The conditional request failed)
```

# 使用次要索引
<a name="ddb-en-client-use-secindex"></a>

次要索引透過定義您在查詢和掃描操作中使用的替代索引鍵來改善資料存取。全域次要索引 (GSI) 具有分割區索引鍵和排序索引鍵，可與基礎資料表上的索引鍵不同。相反地，本機次要索引 (LSI) 會使用主要索引的分割區索引鍵。

## 使用次要索引註釋標註資料類別
<a name="ddb-en-client-use-secindex-annomodel"></a>

參與次要索引的屬性需要 `@DynamoDbSecondaryPartitionKey`或 `@DynamoDbSecondarySortKey`註釋。

以下類別顯示兩個索引的註釋。名為 *SubjectLastPostedDateIndex* 的 GSI 會使用分割區索引鍵的 屬性，以及排序索引鍵`LastPostedDateTime`的 `Subject` 屬性。名為 *ForumLastPostedDateIndex* 的 LSI 會使用 `ForumName`做為分割區索引鍵，並使用 `LastPostedDateTime`做為排序索引鍵。

請注意， `Subject` 屬性提供雙重角色。這是主索引鍵的排序索引鍵，以及名為 *SubjectLastPostedDateIndex* 之 GSI 的分割區索引鍵。

### `MessageThread` 類別
<a name="ddb-en-client-use-secindex-class"></a>

`MessageThread` 類別適用於 *Amazon DynamoDB 開發人員指南*中[範例 Thread 資料表](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/AppendixSampleTables.html)的資料類別。

#### 匯入
<a name="ddb-en-client-use-secindex-classimports"></a>

```
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean;
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.DynamoDbSecondarySortKey;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSortKey;

import java.util.List;
```

```
@DynamoDbBean
public class MessageThread {
    private String ForumName;
    private String Subject;
    private String Message;
    private String LastPostedBy;
    private String LastPostedDateTime;
    private Integer Views;
    private Integer Replies;
    private Integer Answered;
    private List<String> Tags;

    @DynamoDbPartitionKey
    public String getForumName() {
        return ForumName;
    }

    public void setForumName(String forumName) {
        ForumName = forumName;
    }

    // Sort key for primary index and partition key for GSI "SubjectLastPostedDateIndex".
    @DynamoDbSortKey
    @DynamoDbSecondaryPartitionKey(indexNames = "SubjectLastPostedDateIndex")
    public String getSubject() {
        return Subject;
    }

    public void setSubject(String subject) {
        Subject = subject;
    }

    // Sort key for GSI "SubjectLastPostedDateIndex" and sort key for LSI "ForumLastPostedDateIndex".
    @DynamoDbSecondarySortKey(indexNames = {"SubjectLastPostedDateIndex", "ForumLastPostedDateIndex"})
    public String getLastPostedDateTime() {
        return LastPostedDateTime;
    }

    public void setLastPostedDateTime(String lastPostedDateTime) {
        LastPostedDateTime = lastPostedDateTime;
    }
    public String getMessage() {
        return Message;
    }

    public void setMessage(String message) {
        Message = message;
    }

    public String getLastPostedBy() {
        return LastPostedBy;
    }

    public void setLastPostedBy(String lastPostedBy) {
        LastPostedBy = lastPostedBy;
    }

    public Integer getViews() {
        return Views;
    }

    public void setViews(Integer views) {
        Views = views;
    }

    @DynamoDbSecondaryPartitionKey(indexNames = "ForumRepliesIndex")
    public Integer getReplies() {
        return Replies;
    }

    public void setReplies(Integer replies) {
        Replies = replies;
    }

    public Integer getAnswered() {
        return Answered;
    }

    public void setAnswered(Integer answered) {
        Answered = answered;
    }

    public List<String> getTags() {
        return Tags;
    }

    public void setTags(List<String> tags) {
        Tags = tags;
    }

    public MessageThread() {
        this.Answered = 0;
        this.LastPostedBy = "";
        this.ForumName = "";
        this.Message = "";
        this.LastPostedDateTime = "";
        this.Replies = 0;
        this.Views = 0;
        this.Subject = "";
    }

    @Override
    public String toString() {
        return "MessageThread{" +
                "ForumName='" + ForumName + '\'' +
                ", Subject='" + Subject + '\'' +
                ", Message='" + Message + '\'' +
                ", LastPostedBy='" + LastPostedBy + '\'' +
                ", LastPostedDateTime='" + LastPostedDateTime + '\'' +
                ", Views=" + Views +
                ", Replies=" + Replies +
                ", Answered=" + Answered +
                ", Tags=" + Tags +
                '}';
    }
}
```

## 建立索引
<a name="ddb-en-client-use-secindex-confindex"></a>

從適用於 Java 的 SDK 第 2.20.86 版開始， `createTable()`方法會自動從資料類別註釋產生次要索引。根據預設，基礎資料表中的所有屬性都會複製到 索引，而佈建的輸送量值為 20 個讀取容量單位和 20 個寫入容量單位。

不過，如果您使用 2.20.86 之前的 SDK 版本，則需要建置索引與 資料表，如下列範例所示。此範例會建置`Thread`資料表的兩個索引。[建置器](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/CreateTableEnhancedRequest.Builder.html)參數具有設定這兩種索引類型的方法，如註解行 1 和 2 所示。您可以使用索引建置器的 `indexName()`方法，將資料類別註釋中指定的索引名稱與預期的索引類型建立關聯。

此程式碼會將所有資料表屬性設定為在註解行 3 和 4 之後的兩個索引中結束。如需[屬性投影](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LSI.html#LSI.Projections)的詳細資訊，請參閱 *Amazon DynamoDB 開發人員指南*。

```
    public static void createMessageThreadTable(DynamoDbTable<MessageThread> messageThreadDynamoDbTable, DynamoDbClient dynamoDbClient) {
        messageThreadDynamoDbTable.createTable(b -> b
                // 1. Generate the GSI.
                .globalSecondaryIndices(gsi -> gsi.indexName("SubjectLastPostedDateIndex")
                        // 3. Populate the GSI with all attributes.
                        .projection(p -> p
                                .projectionType(ProjectionType.ALL))
                )
                // 2. Generate the LSI.
                .localSecondaryIndices(lsi -> lsi.indexName("ForumLastPostedDateIndex")
                        // 4. Populate the LSI with all attributes.
                        .projection(p -> p
                                .projectionType(ProjectionType.ALL))
                )
        );
```

## 使用 索引查詢
<a name="ddb-en-client-use-secindex-query"></a>

下列範例會查詢本機次要索引 *ForumLastPostedDateIndex*。

在註解行 2 之後，您可以建立呼叫 [DynamoDbIndex.query()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbIndex.html#query(java.util.function.Consumer)) 方法時所需的 [QueryConditional](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/QueryConditional.html) 物件。

透過傳入索引名稱，您可以在註解行 3 之後取得要查詢的索引參考。在註解行 4 之後，您可以在傳入`QueryConditional`物件的索引上呼叫 `query()`方法。

您也可以將查詢設定為傳回三個屬性值，如註解行 5 所示。如果`attributesToProject()`未呼叫 ，查詢會傳回所有屬性值。請注意，指定的屬性名稱以小寫字母開頭。這些屬性名稱符合資料表中使用的屬性名稱，不一定是資料類別的屬性名稱。

在註解行 6 之後，逐一查看結果並記錄查詢傳回的每個項目，並將它存放在清單中以傳回給發起人。

```
public class IndexScanExamples {
    private static Logger logger = LoggerFactory.getLogger(IndexScanExamples.class);

    public static List<MessageThread> queryUsingSecondaryIndices(String lastPostedDate,
                                                                 DynamoDbTable<MessageThread> threadTable) {
        // 1. Log the parameter value.
        logger.info("lastPostedDate value: {}", lastPostedDate);

        // 2. Create a QueryConditional whose sort key value must be greater than or equal to the parameter value.
        QueryConditional queryConditional = QueryConditional.sortGreaterThanOrEqualTo(qc ->
                qc.partitionValue("Forum02").sortValue(lastPostedDate));

        // 3. Specify the index name to query.
        final DynamoDbIndex<MessageThread> forumLastPostedDateIndex = threadTable.index("ForumLastPostedDateIndex");

        // 4. Perform the query using the QueryConditional object.
        final SdkIterable<Page<MessageThread>> pagedResult = forumLastPostedDateIndex.query(q -> q
                .queryConditional(queryConditional)
                // 5. Request three attribute in the results.
                .attributesToProject("forumName", "subject", "lastPostedDateTime"));

        List<MessageThread> collectedItems = new ArrayList<>();
        // 6. Iterate through pages response and sort the items.
        pagedResult.stream().forEach(page -> page.items().stream()
                .sorted(Comparator.comparing(MessageThread::getLastPostedDateTime))
                .forEach(mt -> {
                    // 7. Log the returned items and add the collection to return to the caller.
                    logger.info(mt.toString());
                    collectedItems.add(mt);
                }));
        return collectedItems;
    }
```

在執行查詢之前，資料庫中存在下列項目。

```
MessageThread{ForumName='Forum01', Subject='Subject01', Message='Message01', LastPostedBy='', LastPostedDateTime='2023.03.28', Views=0, Replies=0, Answered=0, Tags=null}
MessageThread{ForumName='Forum02', Subject='Subject02', Message='Message02', LastPostedBy='', LastPostedDateTime='2023.03.29', Views=0, Replies=0, Answered=0, Tags=null}
MessageThread{ForumName='Forum02', Subject='Subject04', Message='Message04', LastPostedBy='', LastPostedDateTime='2023.03.31', Views=0, Replies=0, Answered=0, Tags=null}
MessageThread{ForumName='Forum02', Subject='Subject08', Message='Message08', LastPostedBy='', LastPostedDateTime='2023.04.04', Views=0, Replies=0, Answered=0, Tags=null}
MessageThread{ForumName='Forum02', Subject='Subject10', Message='Message10', LastPostedBy='', LastPostedDateTime='2023.04.06', Views=0, Replies=0, Answered=0, Tags=null}
MessageThread{ForumName='Forum03', Subject='Subject03', Message='Message03', LastPostedBy='', LastPostedDateTime='2023.03.30', Views=0, Replies=0, Answered=0, Tags=null}
MessageThread{ForumName='Forum03', Subject='Subject06', Message='Message06', LastPostedBy='', LastPostedDateTime='2023.04.02', Views=0, Replies=0, Answered=0, Tags=null}
MessageThread{ForumName='Forum03', Subject='Subject09', Message='Message09', LastPostedBy='', LastPostedDateTime='2023.04.05', Views=0, Replies=0, Answered=0, Tags=null}
MessageThread{ForumName='Forum05', Subject='Subject05', Message='Message05', LastPostedBy='', LastPostedDateTime='2023.04.01', Views=0, Replies=0, Answered=0, Tags=null}
MessageThread{ForumName='Forum07', Subject='Subject07', Message='Message07', LastPostedBy='', LastPostedDateTime='2023.04.03', Views=0, Replies=0, Answered=0, Tags=null}
```

第 1 行和第 6 行的記錄陳述式會產生下列主控台輸出。

```
lastPostedDate value: 2023.03.31
MessageThread{ForumName='Forum02', Subject='Subject04', Message='', LastPostedBy='', LastPostedDateTime='2023.03.31', Views=0, Replies=0, Answered=0, Tags=null}
MessageThread{ForumName='Forum02', Subject='Subject08', Message='', LastPostedBy='', LastPostedDateTime='2023.04.04', Views=0, Replies=0, Answered=0, Tags=null}
MessageThread{ForumName='Forum02', Subject='Subject10', Message='', LastPostedBy='', LastPostedDateTime='2023.04.06', Views=0, Replies=0, Answered=0, Tags=null}
```

查詢傳回`forumName`的值為 *Forum02* 且`lastPostedDateTime`值大於或等於 *2023.03.31* 的項目。結果會顯示具有空字串`message`的值，但`message`屬性在索引中具有值。這是因為註解行 5 之後的程式碼未投影訊息屬性。

# 使用進階映射功能
<a name="ddb-en-client-adv-features"></a>

了解 DynamoDB 增強型用戶端 API 中的進階資料表結構描述功能。

## 了解資料表結構描述類型
<a name="ddb-en-client-adv-features-schm-overview"></a>

`[TableSchema](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/TableSchema.html)` 是 DynamoDB 增強型用戶端 API 映射功能的介面。它可以在 [AttributeValues](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/AttributeValue.html) 的映射中映射資料物件。`TableSchema` 物件需要知道要映射之資料表的結構。此結構資訊會存放在 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/TableMetadata.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/TableMetadata.html) 物件中。

增強型用戶端 API 有數個 實作`TableSchema`，如下。

### 從註釋類別產生的資料表結構描述
<a name="ddb-en-client-adv-features-schema-mapped"></a>

從`TableSchema`註釋類別建置 是一項中等昂貴的操作，因此我們建議在應用程式啟動時執行此操作一次。

 [ BeanTableSchema ](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/BeanTableSchema.html)   
此實作是根據 Bean 類別的屬性和註釋建置。此方法的範例會在[入門區段](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-anno-bean)中示範。  
如果 `BeanTableSchema` 的行為不如預期，請啟用 的偵錯記錄`software.amazon.awssdk.enhanced.dynamodb.beans`。

[ImmutableTableSchema](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/ImmutableTableSchema.html)  
此實作是從不可變的資料類別建置而成。此方法說明於 [使用不可變的資料類別](ddb-en-client-use-immut.md)一節。

### 建置器產生的資料表結構描述
<a name="ddb-en-client-adv-features-schema-static"></a>

下列 `TableSchema`是使用建置器從程式碼建置。此方法的成本低於使用註釋資料類別的方法。建置器方法可避免使用註釋，且不需要 JavaBean 命名標準。

[StaticTableSchema](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/StaticTableSchema.html)  
此實作專為可變資料類別而建置。本指南的入門章節示範如何使用[`StaticTableSchema`建置器產生](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-builder) 。

[StaticImmutableTableSchema](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/StaticImmutableTableSchema.html)  
與您建置 的方式類似`StaticTableSchema`，您可以使用`TableSchema`[建置器](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/StaticImmutableTableSchema.html)來產生此類型 的實作，以搭配不可變的資料類別使用。

### 無固定結構描述之資料的資料表結構描述
<a name="ddb-en-client-adv-features-schema-document"></a>

[DocumentTableSchema](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/document/DocumentTableSchema.html)  
與其他 實作不同`TableSchema`，您不定義`DocumentTableSchema`執行個體的屬性。通常，您只能指定主索引鍵和屬性轉換器提供者。`EnhancedDocument` 執行個體提供您從個別元素或 JSON 字串建置的屬性。

# 明確包含或排除屬性
<a name="ddb-en-client-adv-features-inex-attr"></a>

DynamoDB 增強型用戶端 API 提供註釋，以排除資料類別屬性成為資料表上的屬性。透過 API，您也可以使用與資料類別屬性名稱不同的屬性名稱。

## 排除屬性
<a name="ddb-en-client-adv-features-inex-attr-ex"></a>

若要忽略不應映射至 DynamoDB 資料表的屬性，請使用 `@DynamoDbIgnore`註釋標記屬性。

```
private String internalKey;

@DynamoDbIgnore
public String getInternalKey() { return this.internalKey; }
public void setInternalKey(String internalKey) { this.internalKey = internalKey;}
```

## 包含屬性
<a name="ddb-en-client-adv-features-inex-attr-in"></a>

若要變更 DynamoDB 資料表中使用的屬性名稱，請以`@DynamoDbAttribute`註釋標記它，並提供不同的名稱。

```
private String internalKey;

@DynamoDbAttribute("renamedInternalKey")
public String getInternalKey() { return this.internalKey; }
public void setInternalKey(String internalKey) { this.internalKey = internalKey;}
```

# 控制屬性轉換
<a name="ddb-en-client-adv-features-conversion"></a>

根據預設，資料表結構描述會透過 `[AttributeConverterProvider](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/AttributeConverterProvider.html)` 介面的預設實作，為許多常見的 Java 類型提供轉換器。您可以使用自訂 `AttributeConverterProvider` 實作變更整體預設行為。您也可以變更單一屬性的轉換器。

如需可用轉換器的清單，請參閱 [AttributeConverter](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/AttributeConverter.html) 介面 Java 文件。

## 提供自訂屬性轉換器提供者
<a name="ddb-en-client-adv-features-conversion-prov"></a>

您可以透過 `AttributeConverterProvider``@DynamoDbBean``(converterProviders = {…})`註釋提供單一`AttributeConverterProvider`或一組排序的 。任何自訂`AttributeConverterProvider`都必須擴展`AttributeConverterProvider`界面。

請注意，如果您提供自己的屬性轉換器提供者鏈，您將覆寫預設轉換器提供者 `DefaultAttributeConverterProvider`。如果您想要使用 的功能`DefaultAttributeConverterProvider`，則必須將其包含在鏈中。

您也可以使用空陣列 註釋 Bean`{}`。這會停用任何屬性轉換器提供者的使用，包括預設值。在這種情況下，所有要映射的屬性都必須有自己的屬性轉換器。

下列程式碼片段顯示單一轉換器供應商。

```
@DynamoDbBean(converterProviders = ConverterProvider1.class)
public class Customer {

}
```

下列程式碼片段顯示轉換器供應商鏈的使用方式。由於 SDK 預設為最後提供，因此具有最低的優先順序。

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

}
```

靜態資料表結構描述建置器具有以相同方式運作`attributeConverterProviders()`的方法。這會顯示在下列程式碼片段中。

```
private static final StaticTableSchema<Customer> CUSTOMER_TABLE_SCHEMA =
  StaticTableSchema.builder(Customer.class)
    .newItemSupplier(Customer::new)
    .addAttribute(String.class, a -> a.name("name")
                                     a.getter(Customer::getName)
                                     a.setter(Customer::setName))
    .attributeConverterProviders(converterProvider1, converterProvider2)
    .build();
```

## 覆寫單一屬性的映射
<a name="ddb-en-client-adv-features-conversion-single"></a>

若要覆寫單一屬性的映射方式，請提供 屬性`AttributeConverter`的 。此新增會覆寫資料表結構描述`AttributeConverterProviders`中 提供的任何轉換器。這只會為該屬性新增自訂轉換器。除非針對其他屬性明確指定，否則其他屬性，即使是相同類型的屬性也不會使用該轉換器。

`@DynamoDbConvertedBy` 註釋用於指定自訂`AttributeConverter`類別，如下列程式碼片段所示。

```
@DynamoDbBean
public class Customer {
    private String name;

    @DynamoDbConvertedBy(CustomAttributeConverter.class)
    public String getName() { return this.name; }
    public void setName(String name) { this.name = name;}
}
```

靜態結構描述的建置器具有同等屬性建置器`attributeConverter()`方法。此方法採用 的執行個體`AttributeConverter`，如下所示。

```
private static final StaticTableSchema<Customer> CUSTOMER_TABLE_SCHEMA =
  StaticTableSchema.builder(Customer.class)
    .newItemSupplier(Customer::new)
    .addAttribute(String.class, a -> a.name("name")
                                     a.getter(Customer::getName)
                                     a.setter(Customer::setName)
                                     a.attributeConverter(customAttributeConverter))
    .build();
```

## 範例
<a name="ddb-en-client-adv-features-conversion-example"></a>

此範例顯示為[https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/net/HttpCookie.html](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/net/HttpCookie.html)物件提供屬性轉換器的`AttributeConverterProvider`實作。

下列`SimpleUser`類別包含名為 的屬性`lastUsedCookie`，該屬性是 的執行個體`HttpCookie`。

`@DynamoDbBean` 註釋的 參數會列出提供轉換器的兩個`AttributeConverterProvider`類別。

------
#### [ Class with annotations ]

```
    @DynamoDbBean(converterProviders = {CookieConverterProvider.class, DefaultAttributeConverterProvider.class})
    public static final class SimpleUser {
        private String name;
        private HttpCookie lastUsedCookie;

        @DynamoDbPartitionKey
        public String getName() {
            return name;
        }

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

        public HttpCookie getLastUsedCookie() {
            return lastUsedCookie;
        }

        public void setLastUsedCookie(HttpCookie lastUsedCookie) {
            this.lastUsedCookie = lastUsedCookie;
        }
```

------
#### [ Static table schema ]

```
    private static final TableSchema<SimpleUser> SIMPLE_USER_TABLE_SCHEMA =
            TableSchema.builder(SimpleUser.class)
                    .newItemSupplier(SimpleUser::new)
                    .attributeConverterProviders(CookieConverterProvider.create(), AttributeConverterProvider.defaultProvider())
                    .addAttribute(String.class, a -> a.name("name")
                            .setter(SimpleUser::setName)
                            .getter(SimpleUser::getName)
                            .tags(StaticAttributeTags.primaryPartitionKey()))
                    .addAttribute(HttpCookie.class, a -> a.name("lastUsedCookie")
                            .setter(SimpleUser::setLastUsedCookie)
                            .getter(SimpleUser::getLastUsedCookie))
                    .build();
```

------

下列範例中`CookieConverterProvider`的 提供 的執行個體`HttpCookeConverter`。

```
    public static final class CookieConverterProvider implements AttributeConverterProvider {
        private final Map<EnhancedType<?>, AttributeConverter<?>> converterCache = ImmutableMap.of(
                // 1. Add HttpCookieConverter to the internal cache.
                EnhancedType.of(HttpCookie.class), new HttpCookieConverter());

        public static CookieConverterProvider create() {
            return new CookieConverterProvider();
        }

        // The SDK calls this method to find out if the provider contains a AttributeConverter instance
        // for the EnhancedType<T> argument.
        @SuppressWarnings("unchecked")
        @Override
        public <T> AttributeConverter<T> converterFor(EnhancedType<T> enhancedType) {
            return (AttributeConverter<T>) converterCache.get(enhancedType);
        }
    }
```

### 轉換程式碼
<a name="ddb-en-client-adv-features-conversion-example-code"></a>

在下列`HttpCookieConverter`類別的 `transformFrom()`方法中，程式碼會接收`HttpCookie`執行個體，並將其轉換為存放為 屬性的 DynamoDB 映射。

`transformTo()` 方法會接收 DynamoDB `HttpCookie` 映射參數，然後調用需要名稱和值的建構函數。

```
    public static final class HttpCookieConverter implements AttributeConverter<HttpCookie> {

        @Override
        public AttributeValue transformFrom(HttpCookie httpCookie) {

            return AttributeValue.fromM(
            Map.of ("cookieName", AttributeValue.fromS(httpCookie.getName()),
                    "cookieValue", AttributeValue.fromS(httpCookie.getValue()))
            );
        }

        @Override
        public HttpCookie transformTo(AttributeValue attributeValue) {
            Map<String, AttributeValue> map = attributeValue.m();
            return new HttpCookie(
                    map.get("cookieName").s(),
                    map.get("cookieValue").s());
        }

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

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

# 變更屬性的更新行為
<a name="ddb-en-client-adv-features-upd-behavior"></a>

您可以在執行更新操作時自訂個別屬性的*更新*行為。DynamoDB 增強型用戶端 API 中的一些更新操作範例是 [updateItem()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbTable.html#updateItem(T)) 和 [transactWriteItems()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html#transactWriteItems(java.util.function.Consumer))。

例如，假設您想要在記錄上存放*時間戳記上建立*的 。不過，只有在資料庫中沒有屬性的現有值時，才希望寫入其值。在此情況下，您會使用`[WRITE\$1IF\$1NOT\$1EXISTS](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/UpdateBehavior.html#WRITE_IF_NOT_EXISTS)`更新行為。

下列範例顯示將行為新增至 `createdOn` 屬性的註釋。

```
@DynamoDbBean
public class Customer extends GenericRecord {
    private String id;
    private Instant createdOn;

    @DynamoDbPartitionKey
    public String getId() { return this.id; }
    public void setId(String id) { this.name = id; }

    @DynamoDbUpdateBehavior(UpdateBehavior.WRITE_IF_NOT_EXISTS)
    public Instant getCreatedOn() { return this.createdOn; }    
    public void setCreatedOn(Instant createdOn) { this.createdOn = createdOn; }
}
```

您可以在建置靜態資料表結構描述時宣告相同的更新行為，如註解行 1 後的下列範例所示。

```
static final TableSchema<Customer> CUSTOMER_TABLE_SCHEMA =
     TableSchema.builder(Customer.class)
       .newItemSupplier(Customer::new)
       .addAttribute(String.class, a -> a.name("id")
                                         .getter(Customer::getId)
                                         .setter(Customer::setId)
                                         .tags(StaticAttributeTags.primaryPartitionKey()))
       .addAttribute(Instant.class, a -> a.name("createdOn")
                                          .getter(Customer::getCreatedOn)
                                          .setter(Customer::setCreatedOn)
                                          // 1. Add an UpdateBehavior.
                                          .tags(StaticAttributeTags.updateBehavior(UpdateBehavior.WRITE_IF_NOT_EXISTS)))
       .build();
```

# 扁平化其他類別的屬性
<a name="ddb-en-client-adv-features-flatmap"></a>

如果資料表的屬性分散在數個不同的 Java 類別，無論是透過繼承或合成，DynamoDB 增強型用戶端 API 都支援將屬性扁平化為一個類別。

## 使用繼承
<a name="ddb-en-client-adv-features-flatmap-inheritance"></a>

如果您的類別使用繼承，請使用下列方法來扁平化階層。

### 使用註釋的 Bean
<a name="ddb-en-client-adv-features-flatmap-inheritance-anno"></a>

對於註釋方法，兩個類別都必須帶有`@DynamoDbBean`註釋，而類別必須帶有一個或多個主索引鍵註釋。

以下顯示具有繼承關係的資料類別範例。

------
#### [ Standard data class ]

```
@DynamoDbBean
public class Customer extends GenericRecord {
    private String name;

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

@DynamoDbBean
public abstract class GenericRecord {
    private String id;
    private String createdDate;

    @DynamoDbPartitionKey
    public String getId() { return id; }
    public void setId(String id) { this.id = id; }

    public String getCreatedDate() { return createdDate; }
    public void setCreatedDate(String createdDate) { this.createdDate = createdDate; }
}
```

------
#### [ Lombok ]

Lombok [`onMethod`的選項](https://projectlombok.org/features/experimental/onX)會將屬性型 DynamoDB 註釋，例如 `@DynamoDbPartitionKey`，複製到產生的程式碼。

```
@DynamoDbBean
@Data
@ToString(callSuper = true)
public class Customer extends GenericRecord {
    private String name;
}

@Data
@DynamoDbBean
public abstract class GenericRecord {
    @Getter(onMethod_=@DynamoDbPartitionKey)
    private String id;
    private String createdDate;
}
```

------

### 使用靜態結構描述
<a name="ddb-en-client-adv-features-flatmap-inheritance-static"></a>

對於靜態結構描述方法，請使用建置器的 `extend()`方法，將父類別的屬性摺疊至子類別。這會顯示在下列範例中的註解行 1 之後。

```
        StaticTableSchema<org.example.tests.model.inheritance.stat.GenericRecord> GENERIC_RECORD_SCHEMA =
                StaticTableSchema.builder(org.example.tests.model.inheritance.stat.GenericRecord.class)
                        // The partition key will be inherited by the top level mapper.
                        .addAttribute(String.class, a -> a.name("id")
                                .getter(org.example.tests.model.inheritance.stat.GenericRecord::getId)
                                .setter(org.example.tests.model.inheritance.stat.GenericRecord::setId)
                                .tags(primaryPartitionKey()))
                        .addAttribute(String.class, a -> a.name("created_date")
                                .getter(org.example.tests.model.inheritance.stat.GenericRecord::getCreatedDate)
                                .setter(org.example.tests.model.inheritance.stat.GenericRecord::setCreatedDate))
                        .build();

        StaticTableSchema<org.example.tests.model.inheritance.stat.Customer> CUSTOMER_SCHEMA =
                StaticTableSchema.builder(org.example.tests.model.inheritance.stat.Customer.class)
                        .newItemSupplier(org.example.tests.model.inheritance.stat.Customer::new)
                        .addAttribute(String.class, a -> a.name("name")
                                .getter(org.example.tests.model.inheritance.stat.Customer::getName)
                                .setter(org.example.tests.model.inheritance.stat.Customer::setName))
                        // 1. Use the extend() method to collapse the parent attributes onto the child class.
                        .extend(GENERIC_RECORD_SCHEMA)     // All the attributes of the GenericRecord schema are added to Customer.
                        .build();
```

先前的靜態結構描述範例使用下列資料類別。由於映射是在您建置靜態資料表結構描述時定義的，因此資料類別不需要註釋。

#### 資料類別
<a name="gunk"></a>

------
#### [ Standard data class ]

```
public class Customer extends GenericRecord {
    private String name;

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


public abstract class GenericRecord {
    private String id;
    private String createdDate;

    public String getId() { return id; }
    public void setId(String id) { this.id = id; }

    public String getCreatedDate() { return createdDate; }
    public void setCreatedDate(String createdDate) { this.createdDate = createdDate; }
```

------
#### [ Lombok ]

```
@Data
@ToString(callSuper = true)
public class Customer extends GenericRecord{
    private String name;
}

@Data
public abstract class GenericRecord {
    private String id;
    private String createdDate;
}
```

------

## 使用合成
<a name="ddb-en-client-adv-features-flatmap-comp"></a>

如果您的類別使用合成，請使用下列方法來扁平化階層。

### 使用註釋的 Bean
<a name="ddb-en-client-adv-features-flatmap-comp-anno"></a>

`@DynamoDbFlatten` 註釋會扁平化包含的類別。

下列資料類別範例使用`@DynamoDbFlatten`註釋，有效地將包含`GenericRecord`類別的所有屬性新增至`Customer`類別。

------
#### [ Standard data class ]

```
@DynamoDbBean
public class Customer {
    private String name;
    private GenericRecord record;

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

    @DynamoDbFlatten
    public GenericRecord getRecord() { return this.record; }
    public void setRecord(GenericRecord record) { this.record = record; }

@DynamoDbBean
public class GenericRecord {
    private String id;
    private String createdDate;

    @DynamoDbPartitionKey
    public String getId() { return this.id; }
    public void setId(String id) { this.id = id; }

    public String getCreatedDate() { return this.createdDate; }
    public void setCreatedDate(String createdDate) { this.createdDate = createdDate; }
}
```

------
#### [ Lombok ]

```
@Data
@DynamoDbBean
public class Customer {
    private String name;
    @Getter(onMethod_=@DynamoDbFlatten)
    private GenericRecord record;
}

@Data
@DynamoDbBean
public class GenericRecord {
    @Getter(onMethod_=@DynamoDbPartitionKey)
    private String id;
    private String createdDate;
}
```

------

您可以使用平面註釋，視需要平面化任意數量的不同合格類別。以下為目前的限制：
+ 所有屬性名稱在平面化後必須是唯一的。
+ 不得有一個以上的分割區索引鍵、排序索引鍵或資料表名稱。

### 使用靜態結構描述
<a name="ddb-en-client-adv-features-flatmap-comp-static"></a>

當您建置靜態資料表結構描述時，請使用建置器的 `flatten()`方法。您也可以提供識別包含類別的 getter 和 setter 方法。

```
        StaticTableSchema<GenericRecord> GENERIC_RECORD_SCHEMA =
                StaticTableSchema.builder(GenericRecord.class)
                        .newItemSupplier(GenericRecord::new)
                        .addAttribute(String.class, a -> a.name("id")
                                .getter(GenericRecord::getId)
                                .setter(GenericRecord::setId)
                                .tags(primaryPartitionKey()))
                        .addAttribute(String.class, a -> a.name("created_date")
                                .getter(GenericRecord::getCreatedDate)
                                .setter(GenericRecord::setCreatedDate))
                        .build();

        StaticTableSchema<Customer> CUSTOMER_SCHEMA =
                StaticTableSchema.builder(Customer.class)
                        .newItemSupplier(Customer::new)
                        .addAttribute(String.class, a -> a.name("name")
                                .getter(Customer::getName)
                                .setter(Customer::setName))
                        // Because we are flattening a component object, we supply a getter and setter so the
                        // mapper knows how to access it.
                        .flatten(GENERIC_RECORD_SCHEMA, Customer::getRecord, Customer::setRecord)
                        .build();
```

先前的靜態結構描述範例使用下列資料類別。

#### 資料類別
<a name="ddb-en-client-adv-features-flatmap-comp-static-supporting"></a>

------
#### [ Standard data class ]

```
public class Customer {
    private String name;
    private GenericRecord record;

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

    public GenericRecord getRecord() { return this.record; }
    public void setRecord(GenericRecord record) { this.record = record; }

public class GenericRecord {
    private String id;
    private String createdDate;

    public String getId() { return this.id; }
    public void setId(String id) { this.id = id; }

    public String getCreatedDate() { return this.createdDate; }
    public void setCreatedDate(String createdDate) { this.createdDate = createdDate; }
}
```

------
#### [ Lombok ]

```
@Data
public class Customer {
    private String name;
    private GenericRecord record;
}

@Data
public class GenericRecord {
    private String id;
    private String createdDate;
}
```

------

您可以使用建置器模式，視需要水平化任意數量的不同合格類別。

## 對其他程式碼的影響
<a name="ddb-en-client-adv-features-flatmap-compare"></a>

當您使用 `@DynamoDbFlatten` 屬性 （或`flatten()`建置器方法） 時，DynamoDB 中的項目會包含所撰寫物件的每個屬性的屬性。它還包含編寫物件的屬性。

相反地，如果您使用編寫的類別註釋資料類別，但不使用 `@DynamoDbFlatten`，則項目會與編寫的物件一起儲存為單一屬性。

例如，比較平面化中顯示的`Customer`類別與合成範例，包含和不包含`record`屬性的平面化。 [使用註釋的 Bean](#ddb-en-client-adv-features-flatmap-comp-anno)您可以使用 JSON 視覺化差異，如下表所示。


****  

| 使用平面化 | 沒有扁平化 | 
| --- | --- | 
| 3 個屬性 | 2 個屬性 | 
|  <pre>{<br />  "id": "1",<br />  "createdDate": "today",<br />  "name": "my name"<br />}</pre>  |  <pre>{<br />  "id": "1",<br />  "record": {<br />      "createdDate": "today",<br />      "name": "my name"<br />  }<br />}</pre>  | 

如果您有其他程式碼存取預期尋找特定屬性的 DynamoDB 資料表，差異就變得很重要。

# 使用 Bean、地圖、清單和集合等屬性
<a name="ddb-en-client-adv-features-nested"></a>

Bean 定義，例如如下所示的`Person`類別，可能會定義參考具有其他屬性之類型的屬性 （或屬性）。例如，在 `Person`類別中， `mainAddress` 是一個屬性，是指定義其他值屬性的 `Address` Bean。 `addresses` 是指 Java Map，其元素是指 `Address` Bean。這些複雜類型可視為您在 DynamoDB 內容中用於其資料值的簡單屬性容器。

DynamoDB 是指巢狀元素的值屬性，例如地圖、清單或 Bean，做為*巢狀屬性*。[Amazon DynamoDB 開發人員指南](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html#HowItWorks.DataTypes)是指儲存形式的 Java 映射、清單或 Bean 做為*文件類型*。您在 Java 中用於其資料值的簡單屬性在 DynamoDB 中稱為*純量類型*。集合，其中包含相同類型的多個純量元素，稱為*集合類型*。

請務必了解，DynamoDB 增強型用戶端 API 會在儲存時將 Bean 的屬性轉換為 DynamoDB 映射文件類型。

## `Person` 類別
<a name="ddb-en-client-adv-features-nested-person"></a>

```
@DynamoDbBean
public class Person {
    private Integer id;
    private String firstName;
    private String lastName;
    private Integer age;
    private Address mainAddress;
    private Map<String, Address> addresses;
    private List<PhoneNumber> phoneNumbers;
    private Set<String> hobbies;

    @DynamoDbPartitionKey
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Address getMainAddress() {
        return mainAddress;
    }

    public void setMainAddress(Address mainAddress) {
        this.mainAddress = mainAddress;
    }

    public Map<String, Address> getAddresses() {
        return addresses;
    }

    public void setAddresses(Map<String, Address> addresses) {
        this.addresses = addresses;
    }

    public List<PhoneNumber> getPhoneNumbers() {
        return phoneNumbers;
    }

    public void setPhoneNumbers(List<PhoneNumber> phoneNumbers) {
        this.phoneNumbers = phoneNumbers;
    }

    public Set<String> getHobbies() {
        return hobbies;
    }

    public void setHobbies(Set<String> hobbies) {
        this.hobbies = hobbies;
    }

    @Override
    public String toString() {
        return "Person{" +
               "addresses=" + addresses +
               ", id=" + id +
               ", firstName='" + firstName + '\'' +
               ", lastName='" + lastName + '\'' +
               ", age=" + age +
               ", mainAddress=" + mainAddress +
               ", phoneNumbers=" + phoneNumbers +
               ", hobbies=" + hobbies +
               '}';
    }
}
```

## `Address` 類別
<a name="ddb-en-client-adv-features-nested-address"></a>

```
@DynamoDbBean
public class Address {
    private String street;
    private String city;
    private String state;
    private String zipCode;

    public Address() {
    }

    public String getStreet() {
        return this.street;
    }

    public String getCity() {
        return this.city;
    }

    public String getState() {
        return this.state;
    }

    public String getZipCode() {
        return this.zipCode;
    }

    public void setStreet(String street) {
        this.street = street;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public void setState(String state) {
        this.state = state;
    }

    public void setZipCode(String zipCode) {
        this.zipCode = zipCode;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Address address = (Address) o;
        return Objects.equals(street, address.street) && Objects.equals(city, address.city) && Objects.equals(state, address.state) && Objects.equals(zipCode, address.zipCode);
    }

    @Override
    public int hashCode() {
        return Objects.hash(street, city, state, zipCode);
    }

    @Override
    public String toString() {
        return "Address{" +
                "street='" + street + '\'' +
                ", city='" + city + '\'' +
                ", state='" + state + '\'' +
                ", zipCode='" + zipCode + '\'' +
                '}';
    }
}
```

## `PhoneNumber` 類別
<a name="ddb-en-client-adv-features-nested-phonenumber"></a>

```
@DynamoDbBean
public class PhoneNumber {
    String type;
    String number;

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

    @Override
    public String toString() {
        return "PhoneNumber{" +
                "type='" + type + '\'' +
                ", number='" + number + '\'' +
                '}';
    }
}
```

## 儲存複雜類型
<a name="ddb-en-client-adv-features-nested-mapping"></a>

### 使用標註的資料類別
<a name="ddb-en-client-adv-features-nested-map-anno"></a>

您只需註釋自訂類別，即可儲存這些類別的巢狀屬性。先前顯示的`Address`類別和`PhoneNumber`類別只會以`@DynamoDbBean`註釋標註。當 DynamoDB 增強型用戶端 API 使用以下程式碼片段建置`Person`類別的資料表結構描述時，API 會探索 `Address`和 `PhoneNumber`類別的使用，並建置對應的映射以搭配 DynamoDB 使用。

```
TableSchema<Person> personTableSchema = TableSchema.fromBean(Person.class);
```

### 搭配建置器使用抽象結構描述
<a name="ddb-en-client-adv-features-nested-map-builder"></a>

替代方法是針對每個巢狀 Bean 類別使用靜態資料表結構描述建置器，如下列程式碼所示。

`Address` 和 `PhoneNumber`類別的資料表結構描述抽象，因為它們無法與 DynamoDB 資料表搭配使用。這是因為它們缺少主索引鍵的定義。不過，它們會在 `Person`類別的資料表結構描述中用作巢狀結構描述。

在 定義的註解行 1 和 2 之後`PERSON_TABLE_SCHEMA`，您會看到使用抽象資料表結構描述的程式碼。在 `EnhanceType.documentOf(...)` 方法`documentOf`中使用 並不表示 方法傳回 `EnhancedDocument`類型的增強型文件 API。此內容中的 `documentOf(...)`方法會傳回 物件，該物件知道如何使用資料表結構描述引數將其類別引數對應至 DynamoDB 資料表屬性。

#### 靜態結構描述程式碼
<a name="ddb-en-client-adv-features-nested-map-builder-code"></a>

```
    // Abstract table schema that cannot be used to work with a DynamoDB table,
    // but can be used as a nested schema.
    public static final TableSchema<Address> TABLE_SCHEMA_ADDRESS = TableSchema.builder(Address.class)
        .newItemSupplier(Address::new)
        .addAttribute(String.class, a -> a.name("street")
            .getter(Address::getStreet)
            .setter(Address::setStreet))
        .addAttribute(String.class, a -> a.name("city")
            .getter(Address::getCity)
            .setter(Address::setCity))
        .addAttribute(String.class, a -> a.name("zipcode")
            .getter(Address::getZipCode)
            .setter(Address::setZipCode))
        .addAttribute(String.class, a -> a.name("state")
            .getter(Address::getState)
            .setter(Address::setState))
        .build();

    // Abstract table schema that cannot be used to work with a DynamoDB table,
    // but can be used as a nested schema.
    public static final TableSchema<PhoneNumber> TABLE_SCHEMA_PHONENUMBER = TableSchema.builder(PhoneNumber.class)
        .newItemSupplier(PhoneNumber::new)
        .addAttribute(String.class, a -> a.name("type")
            .getter(PhoneNumber::getType)
            .setter(PhoneNumber::setType))
        .addAttribute(String.class, a -> a.name("number")
            .getter(PhoneNumber::getNumber)
            .setter(PhoneNumber::setNumber))
        .build();

    // A static table schema that can be used with a DynamoDB table.
    // The table schema contains two nested schemas that are used to perform mapping to/from DynamoDB.
    public static final TableSchema<Person> PERSON_TABLE_SCHEMA =
        TableSchema.builder(Person.class)
            .newItemSupplier(Person::new)
            .addAttribute(Integer.class, a -> a.name("id")
                .getter(Person::getId)
                .setter(Person::setId)
                .addTag(StaticAttributeTags.primaryPartitionKey()))
            .addAttribute(String.class, a -> a.name("firstName")
                .getter(Person::getFirstName)
                .setter(Person::setFirstName))
            .addAttribute(String.class, a -> a.name("lastName")
                .getter(Person::getLastName)
                .setter(Person::setLastName))
            .addAttribute(Integer.class, a -> a.name("age")
                .getter(Person::getAge)
                .setter(Person::setAge))
            .addAttribute(EnhancedType.documentOf(Address.class, TABLE_SCHEMA_ADDRESS), a -> a.name("mainAddress")
                .getter(Person::getMainAddress)
                .setter(Person::setMainAddress))
            .addAttribute(EnhancedType.listOf(String.class), a -> a.name("hobbies")
                .getter(Person::getHobbies)
                .setter(Person::setHobbies))
            .addAttribute(EnhancedType.mapOf(
                EnhancedType.of(String.class),
                // 1. Use mapping functionality of the Address table schema.
                EnhancedType.documentOf(Address.class, TABLE_SCHEMA_ADDRESS)), a -> a.name("addresses")
                .getter(Person::getAddresses)
                .setter(Person::setAddresses))
            .addAttribute(EnhancedType.listOf(
                // 2. Use mapping functionality of the PhoneNumber table schema.
                EnhancedType.documentOf(PhoneNumber.class, TABLE_SCHEMA_PHONENUMBER)), a -> a.name("phoneNumbers")
                .getter(Person::getPhoneNumbers)
                .setter(Person::setPhoneNumbers))
            .build();
```

## 複雜類型的專案屬性
<a name="ddb-en-client-adv-features-nested-projection"></a>

對於 `query()`和 `scan()`方法，您可以使用 `addNestedAttributeToProject()`和 等方法呼叫，指定要在結果中傳回的屬性`attributesToProject()`。DynamoDB 增強型用戶端 API 在傳送請求之前，會將 Java 方法呼叫參數轉換為[投影表達式](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.ProjectionExpressions.html)。

下列範例會將兩個項目填入`Person`資料表，然後執行三個掃描操作。

第一次掃描會存取資料表中的所有項目，以便將結果與其他掃描操作進行比較。

第二次掃描使用[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/ScanEnhancedRequest.Builder.html#addNestedAttributeToProject(software.amazon.awssdk.enhanced.dynamodb.NestedAttributeName)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/ScanEnhancedRequest.Builder.html#addNestedAttributeToProject(software.amazon.awssdk.enhanced.dynamodb.NestedAttributeName))建置器方法來僅傳回`street`屬性值。

第三個掃描操作使用[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/ScanEnhancedRequest.Builder.html#attributesToProject(java.lang.String...)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/ScanEnhancedRequest.Builder.html#attributesToProject(java.lang.String...))建置器方法來傳回第一層屬性 的資料`hobbies`。的屬性類型`hobbies`是清單。若要存取個別清單項目，請在清單上執行 `get()`操作。

```
        personDynamoDbTable = getDynamoDbEnhancedClient().table("Person", PERSON_TABLE_SCHEMA);
        PersonUtils.createPersonTable(personDynamoDbTable, getDynamoDbClient());
        // Use a utility class to add items to the Person table.
        List<Person> personList = PersonUtils.getItemsForCount(2);
        // This utility method performs a put against DynamoDB to save the instances in the list argument.
        PersonUtils.putCollection(getDynamoDbEnhancedClient(), personList, personDynamoDbTable);

        // The first scan logs all items in the table to compare to the results of the subsequent scans.
        final PageIterable<Person> allItems = personDynamoDbTable.scan();
        allItems.items().forEach(p ->
                // 1. Log what is in the table.
                logger.info(p.toString()));

        // Scan for nested attributes.
        PageIterable<Person> streetScanResult = personDynamoDbTable.scan(b -> b
                // Use the 'addNestedAttributeToProject()' or 'addNestedAttributesToProject()' to access data nested in maps in DynamoDB.
                .addNestedAttributeToProject(
                        NestedAttributeName.create("addresses", "work", "street")
                ));

        streetScanResult.items().forEach(p ->
                //2. Log the results of requesting nested attributes.
                logger.info(p.toString()));

        // Scan for a top-level list attribute.
        PageIterable<Person> hobbiesScanResult = personDynamoDbTable.scan(b -> b
                // Use the 'attributesToProject()' method to access first-level attributes.
                .attributesToProject("hobbies"));

        hobbiesScanResult.items().forEach((p) -> {
            // 3. Log the results of the request for the 'hobbies' attribute.
            logger.info(p.toString());
            // To access an item in a list, first get the parent attribute, 'hobbies', then access items in the list.
            String hobby = p.getHobbies().get(1);
            // 4. Log an item in the list.
            logger.info(hobby);
        });
```

```
// Logged results from comment line 1.
Person{id=2, firstName='first name 2', lastName='last name 2', age=11, addresses={work=Address{street='street 21', city='city 21', state='state 21', zipCode='33333'}, home=Address{street='street 2', city='city 2', state='state 2', zipCode='22222'}}, phoneNumbers=[PhoneNumber{type='home', number='222-222-2222'}, PhoneNumber{type='work', number='333-333-3333'}], hobbies=[hobby 2, hobby 21]}
Person{id=1, firstName='first name 1', lastName='last name 1', age=11, addresses={work=Address{street='street 11', city='city 11', state='state 11', zipCode='22222'}, home=Address{street='street 1', city='city 1', state='state 1', zipCode='11111'}}, phoneNumbers=[PhoneNumber{type='home', number='111-111-1111'}, PhoneNumber{type='work', number='222-222-2222'}], hobbies=[hobby 1, hobby 11]}

// Logged results from comment line 2.
Person{id=null, firstName='null', lastName='null', age=null, addresses={work=Address{street='street 21', city='null', state='null', zipCode='null'}}, phoneNumbers=null, hobbies=null}
Person{id=null, firstName='null', lastName='null', age=null, addresses={work=Address{street='street 11', city='null', state='null', zipCode='null'}}, phoneNumbers=null, hobbies=null}

// Logged results from comment lines 3 and 4.
Person{id=null, firstName='null', lastName='null', age=null, addresses=null, phoneNumbers=null, hobbies=[hobby 2, hobby 21]}
hobby 21
Person{id=null, firstName='null', lastName='null', age=null, addresses=null, phoneNumbers=null, hobbies=[hobby 1, hobby 11]}
hobby 11
```

**注意**  
如果 `attributesToProject()`方法遵循任何其他新增您要投影之屬性的建置器方法，則提供給 的屬性名稱清單會`attributesToProject()`取代所有其他屬性名稱。  
在下列程式碼片段中使用`ScanEnhancedRequest`執行個體執行的掃描，只會傳回愛好資料。  

```
ScanEnhancedRequest lastOverwrites = ScanEnhancedRequest.builder()
        .addNestedAttributeToProject(
                NestedAttributeName.create("addresses", "work", "street"))
        .addAttributeToProject("firstName")
        // If the 'attributesToProject()' method follows other builder methods that add attributes for projection,
        // its list of attributes replace all previous attributes.
        .attributesToProject("hobbies")
        .build();
PageIterable<Person> hobbiesOnlyResult = personDynamoDbTable.scan(lastOverwrites);
hobbiesOnlyResult.items().forEach(p ->
        logger.info(p.toString()));

// Logged results.
Person{id=null, firstName='null', lastName='null', age=null, addresses=null, phoneNumbers=null, hobbies=[hobby 2, hobby 21]}
Person{id=null, firstName='null', lastName='null', age=null, addresses=null, phoneNumbers=null, hobbies=[hobby 1, hobby 11]}
```
下列程式碼片段會先使用 `attributesToProject()`方法。此排序會保留所有其他請求的屬性。  

```
ScanEnhancedRequest attributesPreserved = ScanEnhancedRequest.builder()
        // Use 'attributesToProject()' first so that the method call does not replace all other attributes
        // that you want to project.
        .attributesToProject("firstName")
        .addNestedAttributeToProject(
                NestedAttributeName.create("addresses", "work", "street"))
        .addAttributeToProject("hobbies")
        .build();
PageIterable<Person> allAttributesResult = personDynamoDbTable.scan(attributesPreserved);
allAttributesResult.items().forEach(p ->
        logger.info(p.toString()));

// Logged results.
Person{id=null, firstName='first name 2', lastName='null', age=null, addresses={work=Address{street='street 21', city='null', state='null', zipCode='null'}}, phoneNumbers=null, hobbies=[hobby 2, hobby 21]}
Person{id=null, firstName='first name 1', lastName='null', age=null, addresses={work=Address{street='street 11', city='null', state='null', zipCode='null'}}, phoneNumbers=null, hobbies=[hobby 1, hobby 11]}
```

## 在表達式中使用複雜類型
<a name="ddb-en-client-adv-features-nested-expressions"></a>

您可以在表達式中使用複雜類型，例如篩選條件表達式和條件表達式，方法是使用取消參考運算子來導覽複雜類型的結構。對於物件和映射，請使用 `. (dot)`和 來列出元素 `[n]`（元素序號周圍的方括號）。您無法參考集合的個別元素，但您可以使用 [`contains`函數](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html#Expressions.OperatorsAndFunctions.Functions)。

下列範例顯示掃描操作中使用的兩個篩選條件表達式。篩選條件表達式會指定您希望結果中項目的比對條件。此範例使用先前顯示的 `Address`、 `Person`和 `PhoneNumber`類別。

```
    public void scanUsingFilterOfNestedAttr() {
        // The following is a filter expression for an attribute that is a map of Address objects.
        // By using this filter expression, the SDK returns Person objects that have an address
        // with 'mailing' as a key and 'MS2' for a state value.
        Expression addressFilter = Expression.builder()
                .expression("addresses.#type.#field = :value")
                .putExpressionName("#type", "mailing")
                .putExpressionName("#field", "state")
                .putExpressionValue(":value", AttributeValue.builder().s("MS2").build())
                .build();

        PageIterable<Person> addressFilterResults = personDynamoDbTable.scan(rb -> rb.
                filterExpression(addressFilter));
        addressFilterResults.items().stream().forEach(p -> logger.info("Person: {}", p));

        assert addressFilterResults.items().stream().count() == 1;


        // The following is a filter expression for an attribute that is a list of phone numbers.
        // By using this filter expression, the SDK returns Person objects whose second phone number
        // in the list has a type equal to 'cell'.
        Expression phoneFilter = Expression.builder()
                .expression("phoneNumbers[1].#type = :type")
                .putExpressionName("#type", "type")
                .putExpressionValue(":type", AttributeValue.builder().s("cell").build())
                .build();

        PageIterable<Person> phoneFilterResults = personDynamoDbTable.scan(rb -> rb
                .filterExpression(phoneFilter)
                .attributesToProject("id", "firstName", "lastName", "phoneNumbers")
        );

        phoneFilterResults.items().stream().forEach(p -> logger.info("Person: {}", p));

        assert phoneFilterResults.items().stream().count() == 1;
        assert phoneFilterResults.items().stream().findFirst().get().getPhoneNumbers().get(1).getType().equals("cell");
    }
```

### 填入資料表的協助程式方法
<a name="nested-expressions-helper-method"></a>

```
    public static void populateDatabase() {
        Person person1 = new Person();
        person1.setId(1);
        person1.setFirstName("FirstName1");
        person1.setLastName("LastName1");

        Address billingAddr1 = new Address();
        billingAddr1.setState("BS1");
        billingAddr1.setCity("BillingTown1");

        Address mailing1 = new Address();
        mailing1.setState("MS1");
        mailing1.setCity("MailingTown1");

        person1.setAddresses(Map.of("billing", billingAddr1, "mailing", mailing1));

        PhoneNumber pn1_1 = new PhoneNumber();
        pn1_1.setType("work");
        pn1_1.setNumber("111-111-1111");

        PhoneNumber pn1_2 = new PhoneNumber();
        pn1_2.setType("home");
        pn1_2.setNumber("222-222-2222");

        List<PhoneNumber> phoneNumbers1 = List.of(pn1_1, pn1_2);
        person1.setPhoneNumbers(phoneNumbers1);

        personDynamoDbTable.putItem(person1);

        Person person2 = person1;
        person2.setId(2);
        person2.setFirstName("FirstName2");
        person2.setLastName("LastName2");

        Address billingAddress2 = billingAddr1;
        billingAddress2.setCity("BillingTown2");
        billingAddress2.setState("BS2");

        Address mailing2 = mailing1;
        mailing2.setCity("MailingTown2");
        mailing2.setState("MS2");

        person2.setAddresses(Map.of("billing", billingAddress2, "mailing", mailing2));

        PhoneNumber pn2_1 = new PhoneNumber();
        pn2_1.setType("work");
        pn2_1.setNumber("333-333-3333");

        PhoneNumber pn2_2 = new PhoneNumber();
        pn2_2.setType("cell");
        pn2_2.setNumber("444-444-4444");

        List<PhoneNumber> phoneNumbers2 = List.of(pn2_1, pn2_2);
        person2.setPhoneNumbers(phoneNumbers2);

        personDynamoDbTable.putItem(person2);
    }
```

### 資料庫中項目的 JSON 表示法
<a name="nested-attributes-expression-json-items"></a>

```
{
 "id": 1,
 "addresses": {
  "billing": {
   "city": "BillingTown1",
   "state": "BS1",
   "street": null,
   "zipCode": null
  },
  "mailing": {
   "city": "MailingTown1",
   "state": "MS1",
   "street": null,
   "zipCode": null
  }
 },
 "firstName": "FirstName1",
 "lastName": "LastName1",
 "phoneNumbers": [
  {
   "number": "111-111-1111",
   "type": "work"
  },
  {
   "number": "222-222-2222",
   "type": "home"
  }
 ]
}

{
 "id": 2,
 "addresses": {
  "billing": {
   "city": "BillingTown2",
   "state": "BS2",
   "street": null,
   "zipCode": null
  },
  "mailing": {
   "city": "MailingTown2",
   "state": "MS2",
   "street": null,
   "zipCode": null
  }
 },
 "firstName": "FirstName2",
 "lastName": "LastName2",
 "phoneNumbers": [
  {
   "number": "333-333-3333",
   "type": "work"
  },
  {
   "number": "444-444-4444",
   "type": "cell"
  }
 ]
}
```

## 更新包含複雜類型的項目
<a name="ddb-en-client-adv-features-nested-updates"></a>

若要更新包含複雜類型的項目，您有兩種基本方法：
+ 方法 1：先擷取項目 （使用 `getItem`)、更新物件，然後呼叫 `DynamoDbTable#updateItem`。
+ 方法 2：不要擷取項目，但建構新的執行個體、設定您要更新的屬性，並透過`DynamoDbTable#updateItem`設定適當的 值將執行個體提交至 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/IgnoreNullsMode.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/IgnoreNullsMode.html) 。此方法不需要您在更新項目之前擷取該項目。

本節中顯示的範例使用先前顯示的 `Address`、 `Person`和 `PhoneNumber`類別。

### 更新方法 1：擷取，然後更新
<a name="ddb-en-client-adv-features-nested-updates-retreive"></a>

透過使用此方法，您可以確保更新時不會遺失任何資料。DynamoDB 增強型用戶端 API 會使用儲存在 DynamoDB 中的項目的屬性重新建立 Bean，包括複雜類型的值。然後，您需要使用 getter 和 setter 來更新 bean。此方法的缺點是您先擷取項目所產生的成本。

下列範例示範，如果您在更新項目之前先擷取項目，則不會遺失任何資料。

```
    public void retrieveThenUpdateExample()  {
        // Assume that we ran this code yesterday.
        Person person = new Person();
        person.setId(1);
        person.setFirstName("FirstName");
        person.setLastName("LastName");

        Address mainAddress = new Address();
        mainAddress.setStreet("123 MyStreet");
        mainAddress.setCity("MyCity");
        mainAddress.setState("MyState");
        mainAddress.setZipCode("MyZipCode");
        person.setMainAddress(mainAddress);

        PhoneNumber homePhone = new PhoneNumber();
        homePhone.setNumber("1111111");
        homePhone.setType("HOME");
        person.setPhoneNumbers(List.of(homePhone));

        personDynamoDbTable.putItem(person);

        // Assume that we are running this code now.
        // First, retrieve the item
        Person retrievedPerson = personDynamoDbTable.getItem(Key.builder().partitionValue(1).build());

        // Make any updates.
        retrievedPerson.getMainAddress().setCity("YourCity");

        // Save the updated bean. 'updateItem' returns the bean as it appears after the update.
        Person updatedPerson = personDynamoDbTable.updateItem(retrievedPerson);

        // Verify for this example.
        Address updatedMainAddress = updatedPerson.getMainAddress();
        assert updatedMainAddress.getCity().equals("YourCity");
        assert updatedMainAddress.getState().equals("MyState"); // Unchanged.
        // The list of phone numbers remains; it was not set to null;
        assert updatedPerson.getPhoneNumbers().size() == 1;
    }
```

### 更新方法 2：使用`IgnoreNullsMode`列舉而不先擷取項目
<a name="ddb-en-client-adv-features-nested-updates-nullmode"></a>

若要更新 DynamoDB 中的項目，您可以提供只有要更新屬性的新物件，並將其他值保留為 null。使用此方法時，您需要了解 SDK 如何處理物件中的 null 值，以及如何控制行為。

若要指定您希望 SDK 忽略哪些 null 值屬性，請在建置 時提供`IgnoreNullsMode`列舉[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/UpdateItemEnhancedRequest.Builder.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/UpdateItemEnhancedRequest.Builder.html)。做為使用其中一個列舉值的範例，下列程式碼片段會使用 `IgnoreNullsMode.SCALAR_ONLY` 模式。

```
// Create a new Person object to update the existing item in DynamoDB.
Person personForUpdate = new Person();
personForUpdate.setId(1);
personForUpdate.setFirstName("updatedFirstName");  // 'firstName' is a top scalar property.

Address addressForUpdate = new Address();
addressForUpdate.setCity("updatedCity");
personForUpdate.setMainAddress(addressForUpdate);

personDynamoDbTable.updateItem(r -> r
                .item(personForUpdate)
                .ignoreNullsMode(IgnoreNullsMode.SCALAR_ONLY));

/* With IgnoreNullsMode.SCALAR_ONLY provided, The SDK ignores all null properties. The SDK adds or replaces
the 'firstName' property with the provided value, "updatedFirstName". The SDK updates the 'city' value of
'mainAddress', as long as the 'mainAddress' attribute already exists in DynamoDB.

In the background, the SDK generates an update expression that it sends in the request to DynamoDB.
The following JSON object is a simplified version of what it sends. Notice that the SDK includes the paths
to 'mainAddress.city' and 'firstName' in the SET clause of the update expression. No null values in
'personForUpdate' are included.

{
  "TableName": "PersonTable",
  "Key": {
    "id": {
      "N": "1"
    }
  },
  "ReturnValues": "ALL_NEW",
  "UpdateExpression": "SET #mainAddress.#city = :mainAddress_city, #firstName = :firstName",
  "ExpressionAttributeNames": {
    "#city": "city",
    "#firstName": "firstName",
    "#mainAddress": "mainAddress"
  },
  "ExpressionAttributeValues": {
    ":firstName": {
      "S": "updatedFirstName"
    },
    ":mainAddress_city": {
      "S": "updatedCity"
    }
  }
}

Had we chosen 'IgnoreNullsMode.DEFAULT' instead of 'IgnoreNullsMode.SCALAR_ONLY', the SDK would have included
null values in the "ExpressionAttributeValues" section of the request as shown in the following snippet.

  "ExpressionAttributeValues": {
    ":mainAddress": {
      "M": {
        "zipCode": {
          "NULL": true
        },
        "city": {
          "S": "updatedCity"
        },
        "street": {
          "NULL": true
        },
        "state": {
          "NULL": true
        }
      }
    },
    ":firstName": {
      "S": "updatedFirstName"
    }
  }
*/
```

Amazon DynamoDB 開發人員指南包含[更新表達式](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html)的詳細資訊。

#### `IgnoreNullsMode` 選項的說明
<a name="ignore-nulls-mode-descriptions"></a>
+ `IgnoreNullsMode.SCALAR_ONLY` - 使用此設定更新任何層級的純量屬性。SDK 建構的更新陳述式只會將非 Null 純量屬性傳送至 DynamoDB。SDK 會忽略 Bean 或 Map 的 null 值純量屬性，將儲存的值保留在 DynamoDB 中。

  當您更新地圖或 Bean 的純量屬性時，地圖必須已存在於 DynamoDB 中。如果您將地圖或 Bean 新增至 DynamoDB 中物件尚未存在的物件，您會取得`DynamoDbException`具有訊息的 *更新表達式中提供的文件路徑無效，無法進行更新*。在更新任何屬性之前，您必須使用 `MAPS_ONLY` 模式來新增 Bean 或映射至 DynamoDB。
+ `IgnoreNullsMode.MAPS_ONLY` - 使用此設定來新增或取代 Bean 或 Map 的屬性。SDK 會取代或新增 物件中提供的任何地圖或 Bean。會忽略物件中為 null 的任何 Bean 或映射，保留 DynamoDB 中存在的映射。
+ `IgnoreNullsMode.DEFAULT` - 使用此設定，軟體開發套件永遠不會忽略 null 值。任何 null 層級的純量屬性都會更新為 null。SDK 會將物件中任何 null 值的 Bean、地圖、清單或設定屬性更新為 DynamoDB 中的 null。當您使用此模式時，或未提供模式，因為它是預設模式，您應該先擷取項目，以便 DynamoDB 中的值不會設定為 null，而此值在物件中提供更新，除非您的意圖是將值設定為 null。

在所有模式下，如果您提供具有非空清單或設定的物件`updateItem`給 ，則清單或集合會儲存到 DynamoDB。

#### 為什麼使用 模式？
<a name="ddb-en-client-adv-features-nested-updates-nullmodes-why"></a>

當您為物件提供 Bean 或對應至 `updateItem`方法時，軟體開發套件無法判斷是否應該使用 Bean 中的屬性值 （或對應中的項目值） 來更新項目，或者整個 bean/map 是否應該取代儲存到 DynamoDB 的內容。

使用先前顯示項目先擷取的範例，讓我們嘗試更新 `city` 的屬性，`mainAddress`而不進行擷取。

```
/* The retrieval example saved the Person object with a 'mainAddress' property whose 'city' property value is "MyCity".
/* Note that we create a new Person with only the necessary information to update the city value
of the mainAddress. */
Person personForUpdate = new Person();
personForUpdate.setId(1);
// The update we want to make changes the city.
Address mainAddressForUpdate = new Address();
mainAddressForUpdate.setCity("YourCity");
personForUpdate.setMainAddress(mainAddressForUpdate);

// Lets' try the following:
Person updatedPerson = personDynamoDbTable.updateItem(personForUpdate);
/*
 Since we haven't retrieved the item, we don't know if the 'mainAddress' property
 already exists, so what update expression should the SDK generate?

A) Should it replace or add the 'mainAddress' with the provided object (setting all attributes to null other than city)
   as shown in the following simplified JSON?

      {
        "TableName": "PersonTable",
        "Key": {
          "id": {
            "N": "1"
          }
        },
        "ReturnValues": "ALL_NEW",
        "UpdateExpression": "SET #mainAddress = :mainAddress",
        "ExpressionAttributeNames": {
          "#mainAddress": "mainAddress"
        },
        "ExpressionAttributeValues": {
          ":mainAddress": {
            "M": {
              "zipCode": {
                "NULL": true
              },
              "city": {
                "S": "YourCity"
              },
              "street": {
                "NULL": true
              },
              "state": {
                "NULL": true
              }
            }
          }
        }
      }
 
B) Or should it update only the 'city' attribute of an existing 'mainAddress' as shown in the following simplified JSON?

      {
        "TableName": "PersonTable",
        "Key": {
          "id": {
            "N": "1"
          }
        },
        "ReturnValues": "ALL_NEW",
        "UpdateExpression": "SET #mainAddress.#city = :mainAddress_city",
        "ExpressionAttributeNames": {
          "#city": "city",
          "#mainAddress": "mainAddress"
        },
        "ExpressionAttributeValues": {
          ":mainAddress_city": {
            "S": "YourCity"
          }
        }
      }

However, assume that we don't know if the 'mainAddress' already exists. If it doesn't exist, the SDK would try to update 
an attribute of a non-existent map, which results in an exception.

In this particular case, we would likely select option B (SCALAR_ONLY) to retain the other values of the 'mainAddress'.
*/
```

下列兩個範例顯示 `MAPS_ONLY`和`SCALAR_ONLY`列舉值的使用。 `MAPS_ONLY` 新增地圖並`SCALAR_ONLY`更新地圖。

##### `IgnoreNullsMode.MAPS_ONLY` 範例
<a name="scalar-only-example"></a>

```
    public void mapsOnlyModeExample() {
        // Assume that we ran this code yesterday.
        Person person = new Person();
        person.setId(1);
        person.setFirstName("FirstName");

        personDynamoDbTable.putItem(person);

        // Assume that we are running this code now.

        /* Note that we create a new Person with only the necessary information to update the city value
        of the mainAddress. */
        Person personForUpdate = new Person();
        personForUpdate.setId(1);
        // The update we want to make changes the city.
        Address mainAddressForUpdate = new Address();
        mainAddressForUpdate.setCity("YourCity");
        personForUpdate.setMainAddress(mainAddressForUpdate);


        Person updatedPerson = personDynamoDbTable.updateItem(r -> r
                .item(personForUpdate)
                .ignoreNullsMode(IgnoreNullsMode.MAPS_ONLY)); // Since the mainAddress property does not exist, use MAPS_ONLY mode.
        assert updatedPerson.getMainAddress().getCity().equals("YourCity");
        assert updatedPerson.getMainAddress().getState() == null;
    }
```

##### `IgnoreNullsMode.SCALAR_ONLY example`
<a name="maps-only-example"></a>

```
    public void scalarOnlyExample() {
        // Assume that we ran this code yesterday.
        Person person = new Person();
        person.setId(1);
        Address mainAddress = new Address();
        mainAddress.setCity("MyCity");
        mainAddress.setState("MyState");
        person.setMainAddress(mainAddress);

        personDynamoDbTable.putItem(person);

        // Assume that we are running this code now.

        /* Note that we create a new Person with only the necessary information to update the city value
        of the mainAddress. */
        Person personForUpdate = new Person();
        personForUpdate.setId(1);
        // The update we want to make changes the city.
        Address mainAddressForUpdate = new Address();
        mainAddressForUpdate.setCity("YourCity");
        personForUpdate.setMainAddress(mainAddressForUpdate);

        Person updatedPerson = personDynamoDbTable.updateItem(r -> r
                .item(personForUpdate)
                .ignoreNullsMode(IgnoreNullsMode.SCALAR_ONLY)); // SCALAR_ONLY mode ignores null properties in the in mainAddress.
        assert updatedPerson.getMainAddress().getCity().equals("YourCity");
        assert updatedPerson.getMainAddress().getState().equals("MyState"); // The state property remains the same.
    }
```

請參閱下表，查看每個模式要忽略哪些 null 值。您通常可以使用 `SCALAR_ONLY`和 ，`MAPS_ONLY`但使用 Bean 或 Map 時除外。


**軟體開發套件`updateItem`會忽略每個模式的物件中的哪些 null 值屬性？**  

| 屬性類型 | 在 SCALAR\$1ONLY 模式中 | 在 MAPS\$1ONLY 模式中 | 在 DEFAULT 模式中 | 
| --- | --- | --- | --- | 
| 最高純量 | 是 | 是 | 否 | 
| Bean 或 Map | 是 | 是 | 否 | 
| Bean 或地圖項目的純量值 | 是 1 | 否2 | 否 | 
| 列出或設定 | 是 | 是 | 否 | 

1這假設地圖已存在於 DynamoDB 中。您在物件中提供的 Bean 或對應的任何純量值，無論是否為 null，都需要 DynamoDB 中存在該值的路徑。SDK 在提交請求之前，會使用 dereference Operator `. (dot)` 建構屬性的路徑。

2 由於您使用 `MAPS_ONLY` 模式來完全取代或新增 Bean 或 Map，因此 Bean 或 Map 中的所有 null 值都會保留在儲存至 DynamoDB 的地圖中。

# 使用 保留空物件 `@DynamoDbPreserveEmptyObject`
<a name="ddb-en-client-adv-features-empty"></a>

如果您使用空白物件將 Bean 儲存到 Amazon DynamoDB，而且您希望 SDK 在擷取時重新建立空白物件，請使用 註釋內部 Bean 的 getter`@DynamoDbPreserveEmptyObject`。

為了說明註釋的運作方式，程式碼範例會使用下列兩個 Bean。

## 範例 Bean
<a name="ddb-en-client-adv-features-empty-ex1"></a>

下列資料類別包含兩個`InnerBean`欄位。getter 方法 `getInnerBeanWithoutAnno()`不會以 標註`@DynamoDbPreserveEmptyObject`。`getInnerBeanWithAnno()` 方法會加上註釋。

```
@DynamoDbBean
public class MyBean {

    private String id;
    private String name;
    private InnerBean innerBeanWithoutAnno;
    private InnerBean innerBeanWithAnno;

    @DynamoDbPartitionKey
    public String getId() { return id; }
    public void setId(String id) { this.id = id; }

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

    public InnerBean getInnerBeanWithoutAnno() { return innerBeanWithoutAnno; }
    public void setInnerBeanWithoutAnno(InnerBean innerBeanWithoutAnno) { this.innerBeanWithoutAnno = innerBeanWithoutAnno; }

    @DynamoDbPreserveEmptyObject
    public InnerBean getInnerBeanWithAnno() { return innerBeanWithAnno; }
    public void setInnerBeanWithAnno(InnerBean innerBeanWithAnno) { this.innerBeanWithAnno = innerBeanWithAnno; }

    @Override
    public String toString() {
        return new StringJoiner(", ", MyBean.class.getSimpleName() + "[", "]")
                .add("innerBeanWithoutAnno=" + innerBeanWithoutAnno)
                .add("innerBeanWithAnno=" + innerBeanWithAnno)
                .add("id='" + id + "'")
                .add("name='" + name + "'")
                .toString();
    }
}
```

下列`InnerBean`類別的執行個體是 的欄位，`MyBean`並在範例程式碼中初始化為空物件。

```
@DynamoDbBean
public class InnerBean {

    private String innerBeanField;

    public String getInnerBeanField() {
        return innerBeanField;
    }

    public void setInnerBeanField(String innerBeanField) {
        this.innerBeanField = innerBeanField;
    }

    @Override
    public String toString() {
        return "InnerBean{" +
                "innerBeanField='" + innerBeanField + '\'' +
                '}';
    }
}
```

下列程式碼範例會將具有初始化內部 Bean 的`MyBean`物件儲存至 DynamoDB，然後擷取項目。記錄的輸出顯示 `innerBeanWithoutAnno` 未初始化，但`innerBeanWithAnno`已建立。

```
    public MyBean preserveEmptyObjectAnnoUsingGetItemExample(DynamoDbTable<MyBean> myBeanTable) {
        // Save an item to DynamoDB.
        MyBean bean = new MyBean();
        bean.setId("1");
        bean.setInnerBeanWithoutAnno(new InnerBean());   // Instantiate the inner bean.
        bean.setInnerBeanWithAnno(new InnerBean());      // Instantiate the inner bean.
        myBeanTable.putItem(bean);

        GetItemEnhancedRequest request = GetItemEnhancedRequest.builder()
                .key(Key.builder().partitionValue("1").build())
                .build();
        MyBean myBean = myBeanTable.getItem(request);

        logger.info(myBean.toString());
        // Output 'MyBean[innerBeanWithoutAnno=null, innerBeanWithAnno=InnerBean{innerBeanField='null'}, id='1', name='null']'.

        return myBean;
    }
```

## 替代靜態結構描述
<a name="ddb-en-client-adv-features-empty-ex1-static"></a>

您可以使用下列`StaticTableSchema`版本的資料表結構描述來取代 Bean 上的註釋。

```
    public static TableSchema<MyBean> buildStaticSchemas() {

        StaticTableSchema<InnerBean> innerBeanStaticTableSchema =
                StaticTableSchema.builder(InnerBean.class)
                        .newItemSupplier(InnerBean::new)
                        .addAttribute(String.class, a -> a.name("innerBeanField")
                                .getter(InnerBean::getInnerBeanField)
                                .setter(InnerBean::setInnerBeanField))
                        .build();

        return StaticTableSchema.builder(MyBean.class)
                .newItemSupplier(MyBean::new)
                .addAttribute(String.class, a -> a.name("id")
                        .getter(MyBean::getId)
                        .setter(MyBean::setId)
                        .addTag(primaryPartitionKey()))
                .addAttribute(String.class, a -> a.name("name")
                        .getter(MyBean::getName)
                        .setter(MyBean::setName))
                .addAttribute(EnhancedType.documentOf(InnerBean.class,
                                innerBeanStaticTableSchema),
                        a -> a.name("innerBean1")
                                .getter(MyBean::getInnerBeanWithoutAnno)
                                .setter(MyBean::setInnerBeanWithoutAnno))
                .addAttribute(EnhancedType.documentOf(InnerBean.class,
                                innerBeanStaticTableSchema,
                                b -> b.preserveEmptyObject(true)),
                        a -> a.name("innerBean2")
                                .getter(MyBean::getInnerBeanWithAnno)
                                .setter(MyBean::setInnerBeanWithAnno))
                .build();
    }
```

# 避免儲存巢狀物件的 null 屬性
<a name="ddb-en-client-adv-features-ignore-null"></a>

將資料類別物件儲存至 DynamoDB 時，您可以透過套用`@DynamoDbIgnoreNulls`註釋來略過巢狀物件的 null 屬性。相反地，具有 null 值的頂層屬性絕不會儲存到資料庫。

為了說明註釋的運作方式，程式碼範例會使用下列兩個 Bean。

## 範例 Bean
<a name="ddb-en-client-adv-features-ignore-null-ex1"></a>

下列資料類別包含兩個`InnerBean`欄位。不會註釋 getter 方法 `getInnerBeanWithoutAnno()`。`getInnerBeanWithIgnoreNullsAnno()` 方法會以 標註`@DynamoDbIgnoreNulls`。

```
@DynamoDbBean
public class MyBean {

    private String id;
    private String name;
    private InnerBean innerBeanWithoutAnno;
    private InnerBean innerBeanWithIgnoreNullsAnno;

    @DynamoDbPartitionKey
    public String getId() { return id; }
    public void setId(String id) { this.id = id; }

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

    public InnerBean getInnerBeanWithoutAnno() { return innerBeanWithoutAnno; }
    public void setInnerBeanWithoutAnno(InnerBean innerBeanWithoutAnno) { this.innerBeanWithoutAnno = innerBeanWithoutAnno; }

    @DynamoDbIgnoreNulls
    public InnerBean getInnerBeanWithIgnoreNullsAnno() { return innerBeanWithIgnoreNullsAnno; }
    public void setInnerBeanWithIgnoreNullsAnno(InnerBean innerBeanWithAnno) { this.innerBeanWithIgnoreNullsAnno = innerBeanWithAnno; }

    @Override
    public String toString() {
        return new StringJoiner(", ", MyBean.class.getSimpleName() + "[", "]")
                .add("innerBeanWithoutAnno=" + innerBeanWithoutAnno)
                .add("innerBeanWithIgnoreNullsAnno=" + innerBeanWithIgnoreNullsAnno)
                .add("id='" + id + "'")
                .add("name='" + name + "'")
                .toString();
    }
}
```

下列`InnerBean`類別的執行個體是 的欄位，`MyBean`並用於下列範例程式碼。

```
@DynamoDbBean
public class InnerBean {

    private String innerBeanFieldString;
    private Integer innerBeanFieldInteger;

    public String getInnerBeanFieldString() { return innerBeanFieldString; }
    public void setInnerBeanFieldString(String innerBeanFieldString) { this.innerBeanFieldString = innerBeanFieldString; }

    public Integer getInnerBeanFieldInteger() { return innerBeanFieldInteger; }
    public void setInnerBeanFieldInteger(Integer innerBeanFieldInteger) { this.innerBeanFieldInteger = innerBeanFieldInteger; }

    @Override
    public String toString() {
        return new StringJoiner(", ", InnerBean.class.getSimpleName() + "[", "]")
                .add("innerBeanFieldString='" + innerBeanFieldString + "'")
                .add("innerBeanFieldInteger=" + innerBeanFieldInteger)
                .toString();
    }
}
```

下列程式碼範例會建立 `InnerBean` 物件，並使用 值僅設定其兩個屬性的其中之一。

```
    public void ignoreNullsAnnoUsingPutItemExample(DynamoDbTable<MyBean> myBeanTable) {
        // Create an InnerBean object and give only one attribute a value.
        InnerBean innerBeanOneAttributeSet = new InnerBean();
        innerBeanOneAttributeSet.setInnerBeanFieldInteger(200);

        // Create a MyBean instance and use the same InnerBean instance both for attributes.
        MyBean bean = new MyBean();
        bean.setId("1");
        bean.setInnerBeanWithoutAnno(innerBeanOneAttributeSet);
        bean.setInnerBeanWithIgnoreNullsAnno(innerBeanOneAttributeSet);

        Map<String, AttributeValue> itemMap = myBeanTable.tableSchema().itemToMap(bean, true);
        logger.info(itemMap.toString());
        // Log the map that is sent to the database.
        // {innerBeanWithIgnoreNullsAnno=AttributeValue(M={innerBeanFieldInteger=AttributeValue(N=200)}), id=AttributeValue(S=1), innerBeanWithoutAnno=AttributeValue(M={innerBeanFieldInteger=AttributeValue(N=200), innerBeanFieldString=AttributeValue(NUL=true)})}
        
        // Save the MyBean object to the table.
        myBeanTable.putItem(bean);
    }
```

若要視覺化傳送至 DynamoDB 的低階資料，程式碼會在儲存`MyBean`物件之前記錄屬性映射。

記錄的輸出顯示`innerBeanWithIgnoreNullsAnno`輸出一個屬性，

```
innerBeanWithIgnoreNullsAnno=AttributeValue(M={innerBeanFieldInteger=AttributeValue(N=200)})
```

`innerBeanWithoutAnno` 執行個體會輸出兩個屬性。一個屬性的值為 200，另一個是 null 值的屬性。

```
innerBeanWithoutAnno=AttributeValue(M={innerBeanFieldInteger=AttributeValue(N=200), innerBeanFieldString=AttributeValue(NUL=true)})
```

## 屬性映射的 JSON 表示法
<a name="ddb-en-client-adv-features-ignore-null-ex2"></a>

下列 JSON 表示法可讓您更輕鬆地查看儲存至 DynamoDB 的資料。

```
{
  "id": {
    "S": "1"
  },
  "innerBeanWithIgnoreNullsAnno": {
    "M": {
      "innerBeanFieldInteger": {
        "N": "200"
      }
    }
  },
  "innerBeanWithoutAnno": {
    "M": {
      "innerBeanFieldInteger": {
        "N": "200"
      },
      "innerBeanFieldString": {
        "NULL": true
      }
    }
  }
}
```

## 替代靜態結構描述
<a name="ddb-en-client-adv-features-empty-ex1-static"></a>

您可以使用下列`StaticTableSchema`版本的資料表結構描述來放置資料類別註釋。

```
public static TableSchema<MyBean> buildStaticSchemas() {

    StaticTableSchema<InnerBean> innerBeanStaticTableSchema =
        StaticTableSchema.builder(InnerBean.class)
            .newItemSupplier(InnerBean::new)
            .addAttribute(String.class, a -> a.name("innerBeanFieldString")
                .getter(InnerBean::getInnerBeanFieldString)
                .setter(InnerBean::setInnerBeanFieldString))
            .addAttribute(Integer.class, a -> a.name("innerBeanFieldInteger")
                .getter(InnerBean::getInnerBeanFieldInteger)
                .setter(InnerBean::setInnerBeanFieldInteger))
            .build();

    return StaticTableSchema.builder(MyBean.class)
        .newItemSupplier(MyBean::new)
        .addAttribute(String.class, a -> a.name("id")
            .getter(MyBean::getId)
            .setter(MyBean::setId)
            .addTag(primaryPartitionKey()))
        .addAttribute(String.class, a -> a.name("name")
            .getter(MyBean::getName)
            .setter(MyBean::setName))
        .addAttribute(EnhancedType.documentOf(InnerBean.class,
                innerBeanStaticTableSchema),
            a -> a.name("innerBeanWithoutAnno")
                .getter(MyBean::getInnerBeanWithoutAnno)
                .setter(MyBean::setInnerBeanWithoutAnno))
        .addAttribute(EnhancedType.documentOf(InnerBean.class,
                innerBeanStaticTableSchema,
                b -> b.ignoreNulls(true)),
            a -> a.name("innerBeanWithIgnoreNullsAnno")
                .getter(MyBean::getInnerBeanWithIgnoreNullsAnno)
                .setter(MyBean::setInnerBeanWithIgnoreNullsAnno))
        .build();
}
```

# 使用適用於 DynamoDB 的增強型文件 API 處理 JSON 文件
<a name="ddb-en-client-doc-api"></a>

的[增強型文件 API](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/document/package-summary.html) AWS SDK for Java 2.x 旨在使用沒有固定結構描述的文件導向資料。不過，它也可讓您使用自訂類別來映射個別屬性。

 增強型文件 API 是 適用於 Java 的 AWS SDK v1.x [文件 API](https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/dynamodbv2/document/DynamoDB.html) 的後續版本。

**Contents**
+ [開始使用增強型文件 API](ddb-en-client-doc-api-steps.md)
  + [建立 `DocumentTableSchema`和 `DynamoDbTable`](ddb-en-client-doc-api-steps.md#ddb-en-client-doc-api-steps-createschema)
+ [建置增強型文件](ddb-en-client-doc-api-steps-create-ed.md)
  + [從 JSON 字串建置](ddb-en-client-doc-api-steps-create-ed.md#ddb-en-client-doc-api-steps-create-ed-fromJson)
  + [從個別元素建置](ddb-en-client-doc-api-steps-create-ed.md#ddb-en-client-doc-api-steps-create-ed-fromparts)
+ [執行 CRUD 操作](ddb-en-client-doc-api-steps-use.md)
+ [將增強型文件屬性作為自訂物件存取](ddb-en-client-doc-api-convert.md)
+ [使用`EnhancedDocument`不含 DynamoDB 的](ddb-en-client-doc-api-standalone.md)

# 開始使用增強型文件 API
<a name="ddb-en-client-doc-api-steps"></a>

增強型文件 API 需要與 DynamoDB 增強型用戶端 API 所需的相同[相依性](ddb-en-client-getting-started.md#ddb-en-client-gs-dep)。它還需要[`DynamoDbEnhancedClient`執行個體](ddb-en-client-getting-started-dynamodbTable.md#ddb-en-client-getting-started-dynamodbTable-eclient)，如本主題開頭所示。

由於增強型文件 API 的發行版本為 2.20.3 AWS SDK for Java 2.x，因此您需要該版本或更高版本。

## 建立 `DocumentTableSchema`和 `DynamoDbTable`
<a name="ddb-en-client-doc-api-steps-createschema"></a>

若要使用增強型文件 API 針對 DynamoDB 資料表叫用命令，請將資料表與用戶端 [DynamoDbTable<EnhancedDocument>](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbTable.html) 資源物件建立關聯。

增強型用戶端的 `table()`方法會建立`DynamoDbTable<EnhancedDocument>`執行個體，並需要 DynamoDB 資料表名稱和 的參數`DocumentTableSchema`。

[DocumentTableSchema](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/document/DocumentTableSchema.html) 的建置器需要主要索引鍵和一或多個屬性轉換器提供者。`AttributeConverterProvider.defaultProvider()` 方法提供[預設類型的](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/internal/converter/attribute/package-summary.html)轉換器。即使您提供自訂屬性轉換器提供者，也應該指定它。您可以將選用的次要索引鍵新增至建置器。

下列程式碼片段顯示程式碼，該程式碼會產生存放無結構描述`EnhancedDocument`物件的 DynamoDB `person`資料表的用戶端表示法。

```
DynamoDbTable<EnhancedDocument> documentDynamoDbTable = 
                enhancedClient.table("person",
                        TableSchema.documentSchemaBuilder()
                            // Specify the primary key attributes.
                            .addIndexPartitionKey(TableMetadata.primaryIndexName(),"id", AttributeValueType.S)
                            .addIndexSortKey(TableMetadata.primaryIndexName(), "lastName", AttributeValueType.S)
                            // Specify attribute converter providers. Minimally add the default one.
                            .attributeConverterProviders(AttributeConverterProvider.defaultProvider())
                            .build());
                                                         
// Call documentTable.createTable() if "person" does not exist in DynamoDB.
// createTable() should be called only one time.
```

以下顯示本節中所用`person`物件的 JSON 表示法。

### JSON `person` 物件
<a name="ddb-en-client-doc-api-steps-createschema-obj"></a>

```
{
  "id": 1,
  "firstName": "Richard",
  "lastName": "Roe",
  "age": 25,
  "addresses":
    {
      "home": {
        "zipCode": "00000",
        "city": "Any Town",
        "state": "FL",
        "street": "123 Any Street"
      },
      "work": {
        "zipCode": "00001",
        "city": "Anywhere",
        "state": "FL",
        "street": "100 Main Street"
      }
    },
  "hobbies": [
    "Hobby 1",
    "Hobby 2"
  ],
  "phoneNumbers": [
    {
      "type": "Home",
      "number": "555-0100"
    },
    {
      "type": "Work",
      "number": "555-0119"
    }
  ]
}
```

# 建置增強型文件
<a name="ddb-en-client-doc-api-steps-create-ed"></a>

`[EnhancedDocument](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/document/EnhancedDocument.html)` 代表文件類型物件，具有具有巢狀屬性的複雜結構。`EnhancedDocument` 需要最上層屬性，以符合為 指定的主索引鍵屬性`DocumentTableSchema`。其餘內容是任意的，可以包含頂層屬性和深度巢狀屬性。

您可以使用提供數種新增元素方式的建置器來建立`EnhancedDocument`執行個體。

## 從 JSON 字串建置
<a name="ddb-en-client-doc-api-steps-create-ed-fromJson"></a>

使用 JSON 字串，您可以在一個方法呼叫`EnhancedDocument`中建置 。下列程式碼片段`EnhancedDocument`會從`jsonPerson()`協助程式方法傳回的 JSON 字串建立 。`jsonPerson()` 方法會傳回先前顯示[的人員物件](ddb-en-client-doc-api-steps.md#ddb-en-client-doc-api-steps-createschema-obj)的 JSON 字串版本。

```
EnhancedDocument document = 
        EnhancedDocument.builder()
                        .json( jsonPerson() )
                        .build());
```

## 從個別元素建置
<a name="ddb-en-client-doc-api-steps-create-ed-fromparts"></a>

或者，您可以使用建置器的類型安全方法，從個別元件建置`EnhancedDocument`執行個體。

下列範例會建置`person`增強型文件，類似於上一個範例中從 JSON 字串建置的增強型文件。

```
        /* Define the shape of an address map whose JSON representation looks like the following.
           Use 'addressMapEnhancedType' in the following EnhancedDocument.builder() to simplify the code.
           "home": {
             "zipCode": "00000",
             "city": "Any Town",
             "state": "FL",
             "street": "123 Any Street"
           }*/
        EnhancedType<Map<String, String>> addressMapEnhancedType =
                EnhancedType.mapOf(EnhancedType.of(String.class), EnhancedType.of(String.class));


        //  Use the builder's typesafe methods to add elements to the enhanced document.
        EnhancedDocument personDocument = EnhancedDocument.builder()
                .putNumber("id", 50)
                .putString("firstName", "Shirley")
                .putString("lastName", "Rodriguez")
                .putNumber("age", 53)
                .putNull("nullAttribute")
                .putJson("phoneNumbers", phoneNumbersJSONString())
                /* Add the map of addresses whose JSON representation looks like the following.
                        {
                          "home": {
                            "zipCode": "00000",
                            "city": "Any Town",
                            "state": "FL",
                            "street": "123 Any Street"
                          }
                        } */
                .putMap("addresses", getAddresses(), EnhancedType.of(String.class), addressMapEnhancedType)
                .putList("hobbies", List.of("Theater", "Golf"), EnhancedType.of(String.class))
                .build();
```

### 協助程式方法
<a name="ddb-en-client-doc-api-steps-use-fromparts-helpers"></a>

```
    private static String phoneNumbersJSONString() {
        return "  [" +
                "    {" +
                "      \"type\": \"Home\"," +
                "      \"number\": \"555-0140\"" +
                "    }," +
                "    {" +
                "      \"type\": \"Work\"," +
                "      \"number\": \"555-0155\"" +
                "    }" +
                "  ]";
    }

    private static Map<String, Map<String, String>> getAddresses() {
        return Map.of(
                "home", Map.of(
                        "zipCode", "00002",
                        "city", "Any Town",
                        "state", "ME",
                        "street", "123 Any Street"));

    }
```

# 執行 CRUD 操作
<a name="ddb-en-client-doc-api-steps-use"></a>

定義`EnhancedDocument`執行個體之後，您可以將其儲存至 DynamoDB 資料表。下列程式碼片段使用從個別元素建立的 [personDocument](ddb-en-client-doc-api-steps-create-ed.md#ddb-en-client-doc-api-steps-create-ed-fromparts)。

```
documentDynamoDbTable.putItem(personDocument);
```

從 DynamoDB 讀取增強型文件執行個體後，您可以使用 getter 擷取個別屬性值，如下列程式碼片段所示，以存取從 儲存的資料`personDocument`。或者，您可以將完整內容擷取至 JSON 字串，如範例程式碼的最後部分所示。

```
        // Read the item.
        EnhancedDocument personDocFromDb = documentDynamoDbTable.getItem(Key.builder().partitionValue(50).build());

        // Access top-level attributes.
        logger.info("Name: {} {}", personDocFromDb.getString("firstName"), personDocFromDb.getString("lastName"));
        // Name: Shirley Rodriguez

        // Typesafe access of a deeply nested attribute. The addressMapEnhancedType shown previously defines the shape of an addresses map.
        Map<String, Map<String, String>> addresses = personDocFromDb.getMap("addresses", EnhancedType.of(String.class), addressMapEnhancedType);
        addresses.keySet().forEach(k -> logger.info(addresses.get(k).toString()));
        // {zipCode=00002, city=Any Town, street=123 Any Street, state=ME}

        // Alternatively, work with AttributeValue types checking along the way for deeply nested attributes.
        Map<String, AttributeValue> addressesMap = personDocFromDb.getMapOfUnknownType("addresses");
        addressesMap.keySet().forEach((String k) -> {
            logger.info("Looking at data for [{}] address", k);
            // Looking at data for [home] address
            AttributeValue value = addressesMap.get(k);
            AttributeValue cityValue = value.m().get("city");
            if (cityValue != null) {
                logger.info(cityValue.s());
                // Any Town
            }
        });

        List<AttributeValue> phoneNumbers = personDocFromDb.getListOfUnknownType("phoneNumbers");
        phoneNumbers.forEach((AttributeValue av) -> {
            if (av.hasM()) {
                AttributeValue type = av.m().get("type");
                if (type.s() != null) {
                    logger.info("Type of phone: {}", type.s());
                    // Type of phone: Home
                    // Type of phone: Work
                }
            }
        });

        String jsonPerson = personDocFromDb.toJson();
        logger.info(jsonPerson);
        // {"firstName":"Shirley","lastName":"Rodriguez","addresses":{"home":{"zipCode":"00002","city":"Any Town","street":"123 Any Street","state":"ME"}},"hobbies":["Theater","Golf"],
        //     "id":50,"nullAttribute":null,"age":53,"phoneNumbers":[{"number":"555-0140","type":"Home"},{"number":"555-0155","type":"Work"}]}
```

`EnhancedDocument` 執行個體可與 `[DynamoDbTable](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbTable.html)`或 [DynamoDbEnhancedClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html) 的任何方法搭配使用，以取代映射的資料類別。

# 將增強型文件屬性作為自訂物件存取
<a name="ddb-en-client-doc-api-convert"></a>

除了提供 API 來讀取和寫入具有無結構描述結構的屬性之外，增強型文件 API 還可讓您在自訂類別的執行個體之間轉換屬性。

增強型文件 API 使用[控制屬性轉換](ddb-en-client-adv-features-conversion.md)區段中顯示的 `AttributeConverterProvider`和 `AttributeConverter`，作為 DynamoDB 增強型用戶端 API 的一部分。

在下列範例中，我們使用 `CustomAttributeConverterProvider`搭配其巢狀`AddressConverter`類別來轉換`Address`物件。

此範例顯示您可以混合來自 類別的資料，也可以混合來自視需要建置之結構的資料。此範例也顯示自訂類別可用於巢狀結構的任何層級。此範例中的`Address`物件是映射中使用的值。

```
    public static void attributeToAddressClassMappingExample(DynamoDbEnhancedClient enhancedClient, DynamoDbClient standardClient) {
        String tableName = "customer";

        // Define the DynamoDbTable for an enhanced document.
        // The schema builder provides methods for attribute converter providers and keys.
        DynamoDbTable<EnhancedDocument> documentDynamoDbTable = enhancedClient.table(tableName,
                DocumentTableSchema.builder()
                        // Add the CustomAttributeConverterProvider along with the default when you build the table schema.
                        .attributeConverterProviders(
                                List.of(
                                        new CustomAttributeConverterProvider(),
                                        AttributeConverterProvider.defaultProvider()))
                        .addIndexPartitionKey(TableMetadata.primaryIndexName(), "id", AttributeValueType.N)
                        .addIndexSortKey(TableMetadata.primaryIndexName(), "lastName", AttributeValueType.S)
                        .build());
        // Create the DynamoDB table if needed.
        documentDynamoDbTable.createTable();
        waitForTableCreation(tableName, standardClient);


        // The getAddressesForCustomMappingExample() helper method that provides 'addresses' shows the use of a custom Address class
        // rather than using a Map<String, Map<String, String> to hold the address data.
        Map<String, Address> addresses = getAddressesForCustomMappingExample();

        // Build an EnhancedDocument instance to save an item with a mix of structures defined as needed and static classes.
        EnhancedDocument personDocument = EnhancedDocument.builder()
                .putNumber("id", 50)
                .putString("firstName", "Shirley")
                .putString("lastName", "Rodriguez")
                .putNumber("age", 53)
                .putNull("nullAttribute")
                .putJson("phoneNumbers", phoneNumbersJSONString())
                // Note the use of 'EnhancedType.of(Address.class)' instead of the more generic
                // 'EnhancedType.mapOf(EnhancedType.of(String.class), EnhancedType.of(String.class))' that was used in a previous example.
                .putMap("addresses", addresses, EnhancedType.of(String.class), EnhancedType.of(Address.class))
                .putList("hobbies", List.of("Hobby 1", "Hobby 2"), EnhancedType.of(String.class))
                .build();
        // Save the item to DynamoDB.
        documentDynamoDbTable.putItem(personDocument);

        // Retrieve the item just saved.
        EnhancedDocument srPerson = documentDynamoDbTable.getItem(Key.builder().partitionValue(50).sortValue("Rodriguez").build());

        // Access the addresses attribute.
        Map<String, Address> srAddresses = srPerson.get("addresses",
                EnhancedType.mapOf(EnhancedType.of(String.class), EnhancedType.of(Address.class)));

        srAddresses.keySet().forEach(k -> logger.info(addresses.get(k).toString()));

        documentDynamoDbTable.deleteTable();

// The content logged to the console shows that the saved maps were converted to Address instances.
Address{street='123 Main Street', city='Any Town', state='NC', zipCode='00000'}
Address{street='100 Any Street', city='Any Town', state='NC', zipCode='00000'}
```

## `CustomAttributeConverterProvider`代碼
<a name="ddb-en-client-doc-api-convert-provider"></a>

```
public class CustomAttributeConverterProvider implements AttributeConverterProvider {

    private final Map<EnhancedType<?>, AttributeConverter<?>> converterCache = ImmutableMap.of(
            // 1. Add AddressConverter to the internal cache.
            EnhancedType.of(Address.class), new AddressConverter());

    public static CustomAttributeConverterProvider create() {
        return new CustomAttributeConverterProvider();
    }

    // 2. The enhanced client queries the provider for attribute converters if it
    //    encounters a type that it does not know how to convert.
    @SuppressWarnings("unchecked")
    @Override
    public <T> AttributeConverter<T> converterFor(EnhancedType<T> enhancedType) {
        return (AttributeConverter<T>) converterCache.get(enhancedType);
    }

    // 3. Custom attribute converter
    private class AddressConverter implements AttributeConverter<Address> {
        // 4. Transform an Address object into a DynamoDB map.
        @Override
        public AttributeValue transformFrom(Address address) {

            Map<String, AttributeValue> attributeValueMap = Map.of(
                    "street", AttributeValue.fromS(address.getStreet()),
                    "city", AttributeValue.fromS(address.getCity()),
                    "state", AttributeValue.fromS(address.getState()),
                    "zipCode", AttributeValue.fromS(address.getZipCode()));

            return AttributeValue.fromM(attributeValueMap);
        }

        // 5. Transform the DynamoDB map attribute to an Address oject.
        @Override
        public Address transformTo(AttributeValue attributeValue) {
            Map<String, AttributeValue> m = attributeValue.m();
            Address address = new Address();
            address.setStreet(m.get("street").s());
            address.setCity(m.get("city").s());
            address.setState(m.get("state").s());
            address.setZipCode(m.get("zipCode").s());

            return address;
        }

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

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

## `Address` 類別
<a name="ddb-en-client-doc-api-convert-address"></a>

```
public class Address {
                  private String street;
                  private String city;
                  private String state;
                  private String zipCode;

                  public Address() {
                  }

                  public String getStreet() {
                  return this.street;
                  }

                  public String getCity() {
                  return this.city;
                  }

                  public String getState() {
                  return this.state;
                  }

                  public String getZipCode() {
                  return this.zipCode;
                  }

                  public void setStreet(String street) {
                  this.street = street;
                  }

                  public void setCity(String city) {
                  this.city = city;
                  }

                  public void setState(String state) {
                  this.state = state;
                  }

                  public void setZipCode(String zipCode) {
                  this.zipCode = zipCode;
                  }
                  }
```

## 提供地址的協助程式方法
<a name="ddb-en-client-doc-api-convert-helper"></a>

下列協助程式方法提供使用自訂`Address`執行個體做為值的映射，而非一般`Map<String, String>`執行個體做為值。

```
    private static Map<String, Address> getAddressesForCustomMappingExample() {
        Address homeAddress = new Address();
        homeAddress.setStreet("100 Any Street");
        homeAddress.setCity("Any Town");
        homeAddress.setState("NC");
        homeAddress.setZipCode("00000");

        Address workAddress = new Address();
        workAddress.setStreet("123 Main Street");
        workAddress.setCity("Any Town");
        workAddress.setState("NC");
        workAddress.setZipCode("00000");

        return Map.of("home", homeAddress,
                "work", workAddress);
    }
```

# 使用`EnhancedDocument`不含 DynamoDB 的
<a name="ddb-en-client-doc-api-standalone"></a>

雖然您通常使用 執行個體`EnhancedDocument`來讀取和寫入文件類型的 DynamoDB 項目，但它也可以獨立於 DynamoDB 使用。

您可以使用 `EnhancedDocuments` 在 JSON 字串或自訂物件之間轉換為低階映射的 ，`AttributeValues`如下列範例所示。

```
    public static void conversionWithoutDynamoDbExample() {
        Address address = new Address();
        address.setCity("my city");
        address.setState("my state");
        address.setStreet("my street");
        address.setZipCode("00000");

        // Build an EnhancedDocument instance for its conversion functionality alone.
        EnhancedDocument addressEnhancedDoc = EnhancedDocument.builder()
                // Important: You must specify attribute converter providers when you build an EnhancedDocument instance not used with a DynamoDB table.
                .attributeConverterProviders(new CustomAttributeConverterProvider(), DefaultAttributeConverterProvider.create())
                .put("addressDoc", address, Address.class)
                .build();

        // Convert address to a low-level item representation.
        final Map<String, AttributeValue> addressAsAttributeMap = addressEnhancedDoc.getMapOfUnknownType("addressDoc");
        logger.info("addressAsAttributeMap: {}", addressAsAttributeMap.toString());

        // Convert address to a JSON string.
        String addressAsJsonString = addressEnhancedDoc.getJson("addressDoc");
        logger.info("addressAsJsonString: {}", addressAsJsonString);
        // Convert addressEnhancedDoc back to an Address instance.
        Address addressConverted =  addressEnhancedDoc.get("addressDoc", Address.class);
        logger.info("addressConverted: {}", addressConverted.toString());
    }

   /* Console output:
          addressAsAttributeMap: {zipCode=AttributeValue(S=00000), state=AttributeValue(S=my state), street=AttributeValue(S=my street), city=AttributeValue(S=my city)}
          addressAsJsonString: {"zipCode":"00000","state":"my state","street":"my street","city":"my city"}
          addressConverted: Address{street='my street', city='my city', state='my state', zipCode='00000'}
   */
```

**注意**  
當您使用獨立於 DynamoDB 資料表的增強型文件時，請務必在建置器上明確設定屬性轉換器提供者。  
相反地，當增強型文件與 DynamoDB 資料表搭配使用時，文件資料表結構描述會提供轉換器提供者。

# 使用擴充功能自訂 DynamoDB 增強型用戶端操作
<a name="ddb-en-client-extensions"></a>

DynamoDB 增強型用戶端 API 支援外掛程式擴充功能，可提供映射操作以外的功能。延伸模組使用兩種勾點方法來修改讀取和寫入操作期間的資料：
+ `beforeWrite()` - 在寫入操作發生之前對其進行修改
+ `afterRead()` - 修改讀取操作發生後的結果

有些操作 （例如項目更新） 同時執行寫入和讀取，因此會呼叫這兩種勾點方法。

## 如何載入延伸模組
<a name="ddb-en-client-extensions-loading"></a>

延伸項目會依照您在增強型用戶端建置器中指定的順序載入。負載順序可能很重要，因為一個延伸模組可以對先前延伸模組轉換的值採取行動。

根據預設，增強型用戶端會載入兩個延伸模組：
+ `[VersionedRecordExtension](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/extensions/VersionedRecordExtension.html)` - 提供樂觀鎖定
+ `[AtomicCounterExtension](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/extensions/AtomicCounterExtension.html)` - 自動遞增計數器屬性

您可以使用增強型用戶端建置器覆寫預設行為，並載入任何延伸。如果您不想要預設副檔名，也可以不指定任何副檔名。

**重要**  
如果您載入自己的擴充功能，增強型用戶端不會載入任何預設擴充功能。如果您想要任一預設延伸模組提供的行為，您需要將其明確新增至延伸模組清單。

下列範例顯示如何載入名為 `verifyChecksumExtension`的自訂延伸`VersionedRecordExtension`模組。此範例中`AtomicCounterExtension`不會載入 。

```
DynamoDbEnhancedClientExtension versionedRecordExtension = VersionedRecordExtension.builder().build();

DynamoDbEnhancedClient enhancedClient = 
    DynamoDbEnhancedClient.builder()
                          .dynamoDbClient(dynamoDbClient)
                          .extensions(versionedRecordExtension, verifyChecksumExtension)
                          .build();
```

## 可用的延伸模組詳細資訊和組態
<a name="ddb-en-client-extensions-details"></a>

下列各節提供 SDK 中每個可用延伸模組的詳細資訊。

### 使用 實作樂觀鎖定 `VersionedRecordExtension`
<a name="ddb-en-client-extensions-VRE"></a>

`VersionedRecordExtension` 延伸項目會在項目寫入資料庫時遞增和追蹤項目版本號碼，以提供樂觀的鎖定。如果實際持續項目的版本編號與應用程式上次讀取的值不符，則每個寫入都會新增一個條件，導致寫入失敗。

#### Configuration
<a name="ddb-en-client-extensions-VRE-conf"></a>

若要指定要用來追蹤項目版本編號的屬性，請在資料表結構描述中標記數值屬性。

下列程式碼片段指定 `version` 屬性應保留項目版本編號。

```
    @DynamoDbVersionAttribute
    public Integer getVersion() {...};
    public void setVersion(Integer version) {...};
```

等效的靜態資料表結構描述方法會顯示在下列程式碼片段中。

```
    .addAttribute(Integer.class, a -> a.name("version")
                                       .getter(Customer::getVersion)
                                       .setter(Customer::setVersion)
                                        // Apply the 'version' tag to the attribute.
                                       .tags(VersionedRecordExtension.AttributeTags.versionAttribute())
```

#### 運作方式
<a name="ddb-en-client-extensions-VRE-how-it-works"></a>

使用 的樂觀鎖定對這些 `DynamoDbEnhancedClient`和 `DynamoDbTable`方法`VersionedRecordExtension`有下列影響：

**`putItem`**  
新項目會獲指派初始版本值 0。這可以使用 設定`@DynamoDbVersionAttribute(startAt = X)`。

**`updateItem`**  
如果您擷取項目、更新其一或多個屬性，並嘗試儲存變更，則只有在用戶端和伺服器端的版本編號相符時，操作才會成功。  
如果成功，版本編號會自動遞增 1。這可以使用 設定`@DynamoDbVersionAttribute(incrementBy = X)`。

**`deleteItem`**  
`DynamoDbVersionAttribute` 註釋沒有效果。刪除項目時，您必須手動新增條件表達式。  
下列範例新增條件式表達式，以確保已刪除的項目是已讀取的項目。在下列範例中`recordVersion`， Bean 的 屬性以 標註`@DynamoDbVersionAttribute`。  

```
// 1. Read the item and get its current version.
Customer item = customerTable.getItem(Key.builder().partitionValue("someId").build());
// `recordVersion` is the bean's attribute that is annotated with `@DynamoDbVersionAttribute`.
AttributeValue currentVersion = item.getRecordVersion();

// 2. Create conditional delete with the `currentVersion` value.
DeleteItemEnhancedRequest deleteItemRequest =
    DeleteItemEnhancedRequest.builder()
       .key(KEY)
       .conditionExpression(Expression.builder()
           .expression("recordVersion = :current_version_value")
           .putExpressionValue(":current_version_value", currentVersion)
           .build()).build();

customerTable.deleteItem(deleteItemRequest);
```

**`transactWriteItems`**  
+ `addPutItem`：此方法具有與 相同的行為`putItem`。
+ `addUpdateItem`：此方法具有與 相同的行為`updateItem`。
+ `addDeleteItem`：此方法具有與 相同的行為`deleteItem`。

**`batchWriteItem`**  
+ `addPutItem`：此方法具有與 相同的行為`putItem`。
+ `addDeleteItem`：此方法具有與 相同的行為`deleteItem`。

**注意**  
DynamoDB 全域資料表在並行更新之間使用[「最後寫入器獲勝」對帳](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/V2globaltables_HowItWorks.html#V2globaltables_HowItWorks.consistency-modes)，其中 DynamoDB 會盡最大努力判斷最後寫入器。如果您使用全域資料表，此「上次寫入器獲勝」政策表示鎖定策略可能無法如預期運作，因為所有複本最終會根據 DynamoDB 決定的上次寫入收斂。

#### 如何停用
<a name="ddb-en-client-extensions-VRE-how-to-disable"></a>

若要停用樂觀鎖定，請勿使用 `@DynamoDbVersionAttribute`註釋。

### 使用 實作計數器 `AtomicCounterExtension`
<a name="ddb-en-client-extensions-ACE"></a>

每次記錄寫入資料庫時，`AtomicCounterExtension`延伸項目都會遞增標記的數值屬性。您可以指定開始值和增量值。如果未指定任何值，則開始值會設為 0，而屬性的值會遞增 1。

#### Configuration
<a name="ddb-en-client-extensions-ACE-conf"></a>

若要指定哪個屬性是計數器，請在資料表結構描述`Long`中標記 類型的屬性。

下列程式碼片段顯示 `counter` 屬性的預設開始值和增量值的使用方式。

```
    @DynamoDbAtomicCounter
    public Long getCounter() {...};
    public void setCounter(Long counter) {...};
```

靜態資料表結構描述方法會顯示在下列程式碼片段中。原子計數器延伸使用 10 的開始值，每次寫入記錄時都會將值增加 5。

```
    .addAttribute(Integer.class, a -> a.name("counter")
                                       .getter(Customer::getCounter)
                                       .setter(Customer::setCounter)
                                        // Apply the 'atomicCounter' tag to the attribute with start and increment values.
                                       .tags(StaticAttributeTags.atomicCounter(10L, 5L))
```

### 使用 新增時間戳記 `AutoGeneratedTimestampRecordExtension`
<a name="ddb-en-client-extensions-AGTE"></a>

每次項目成功寫入資料庫時，`AutoGeneratedTimestampRecordExtension`延伸項目會自動`[Instant](https://docs.oracle.com/javase/8/docs/api/java/time/Instant.html)`使用目前時間戳記更新 類型的標記屬性。預設不會載入此延伸模組。

#### Configuration
<a name="ddb-en-client-extensions-AGTE-conf"></a>

若要指定要使用目前時間戳記更新哪個屬性，請在資料表結構描述中標記 `Instant` 屬性。

`lastUpdate` 屬性是下列程式碼片段中延伸項目行為的目標。請注意， 屬性必須是 `Instant`類型的要求。

```
    @DynamoDbAutoGeneratedTimestampAttribute
    public Instant getLastUpdate() {...}
    public void setLastUpdate(Instant lastUpdate) {...}
```

等效的靜態資料表結構描述方法會顯示在下列程式碼片段中。

```
     .addAttribute(Instant.class, a -> a.name("lastUpdate")
                                        .getter(Customer::getLastUpdate)
                                        .setter(Customer::setLastUpdate)
                                        // Applying the 'autoGeneratedTimestamp' tag to the attribute.
                                        .tags(AutoGeneratedTimestampRecordExtension.AttributeTags.autoGeneratedTimestampAttribute())
```

### 使用 AutoGeneratedUuidExtension 產生 UUID
<a name="ddb-en-client-extensions-AGUE"></a>

`AutoGeneratedUuidExtension` 新記錄寫入資料庫時，延伸項目會產生屬性的唯一 UUID （通用唯一識別符）。使用 Java JDK [UUID.randomUUID()](https://docs.oracle.com/javase/8/docs/api/java/util/UUID.html#randomUUID--) 方法，並套用到 類型的屬性`java.lang.String`。預設不會載入此延伸模組。

#### Configuration
<a name="ddb-en-client-extensions-AGUE-conf"></a>

`uniqueId` 屬性是下列程式碼片段中延伸項目行為的目標。

```
    @AutoGeneratedUuidExtension
    public String getUniqueId() {...}
    public void setUniqueId(String uniqueId) {...}
```

等效的靜態資料表結構描述方法會顯示在下列程式碼片段中。

```
     .addAttribute(String.class, a -> a.name("uniqueId")
                                        .getter(Customer::getUniqueId)
                                        .setter(Customer::setUniqueId)
                                        // Applying the 'autoGeneratedUuid' tag to the attribute.
                                        .tags(AutoGeneratedUuidExtension.AttributeTags.autoGeneratedUuidAttribute())
```

如果您希望延伸模組僅針對 `putItem` 方法填入 UUID，而非針對 `updateItem`方法填入 UUID，請新增[更新行為](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/UpdateBehavior.html)註釋，如下列程式碼片段所示。

```
    @AutoGeneratedUuidExtension
    @DynamoDbUpdateBehavior(UpdateBehavior.WRITE_IF_NOT_EXISTS)
    public String getUniqueId() {...}
    public void setUniqueId(String uniqueId) {...}
```

如果您使用靜態資料表結構描述方法，請使用下列同等程式碼。

```
     .addAttribute(String.class, a -> a.name("uniqueId")
                                        .getter(Customer::getUniqueId)
                                        .setter(Customer::setUniqueId)
                                        // Applying the 'autoGeneratedUuid' tag to the attribute.
                                        .tags(AutoGeneratedUuidExtension.AttributeTags.autoGeneratedUuidAttribute(),
                                              StaticAttributeTags.updateBehavior(UpdateBehavior.WRITE_IF_NOT_EXISTS))
```

# 自訂延伸模組範例
<a name="ddb-en-client-extensions-custom"></a>

您可以實作 `DynamoDbEnhancedClientExtension`界面來建立自訂擴充功能。下列自訂延伸類別顯示一種`beforeWrite()`方法，如果資料庫中的項目還沒有`registrationDate`更新表達式，則使用更新表達式來設定屬性。

```
public final class CustomExtension implements DynamoDbEnhancedClientExtension {

    // 1. In a custom extension, use an UpdateExpression to define what action to take before
    //    an item is updated.
    @Override
    public WriteModification beforeWrite(DynamoDbExtensionContext.BeforeWrite context) {
        if ( context.operationContext().tableName().equals("Customer")
                && context.operationName().equals(OperationName.UPDATE_ITEM)) {
            return WriteModification.builder()
                    .updateExpression(createUpdateExpression())
                    .build();
        }
        return WriteModification.builder().build();  // Return an "empty" WriteModification instance if the extension should not be applied.
                                                     // In this case, if the code is not updating an item on the Customer table.
    }

    private static UpdateExpression createUpdateExpression() {

        // 2. Use a SetAction, a subclass of UpdateAction, to provide the values in the update.
        SetAction setAction =
                SetAction.builder()
                        .path("registrationDate")
                        .value("if_not_exists(registrationDate, :regValue)")
                        .putExpressionValue(":regValue", AttributeValue.fromS(Instant.now().toString()))
                        .build();
        // 3. Build the UpdateExpression with one or more UpdateAction.
        return UpdateExpression.builder()
                .addAction(setAction)
                .build();
    }
}
```

# 非同步使用 DynamoDB 增強型用戶端 API
<a name="ddb-en-client-async"></a>

如果您的應用程式需要非封鎖、非同步呼叫 DynamoDB，您可以使用 [DynamoDbEnhancedAsyncClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedAsyncClient.html)。它類似於同步實作，但有下列主要差異：

1. 建置 時`DynamoDbEnhancedAsyncClient`，您必須提供標準用戶端的非同步版本 `DynamoDbAsyncClient`，如下列程式碼片段所示。

   ```
    DynamoDbEnhancedAsyncClient enhancedClient = 
        DynamoDbEnhancedAsyncClient.builder()
                                   .dynamoDbClient(dynamoDbAsyncClient)
                                   .build();
   ```

1. 傳回單一資料物件的方法會傳回結果`CompletableFuture`的 ，而非僅傳回結果。然後，您的應用程式可以執行其他工作，而不必封鎖結果。下列程式碼片段顯示非同步`getItem()`方法。

   ```
   CompletableFuture<Customer> result = customerDynamoDbTable.getItem(customer);
   // Perform other work here.
   return result.join();   // Now block and wait for the result.
   ```

1. 傳回結果分頁清單的方法會傳回[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/pagination/sync/SdkIterable.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/pagination/sync/SdkIterable.html)同步針對相同方法`DynamoDbEnhanceClient`傳回的 ，[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/SdkPublisher.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/SdkPublisher.html)而不是 。然後，您的應用程式可以訂閱該發佈者的處理常式，以非同步方式處理結果，而無需封鎖。

   ```
   PagePublisher<Customer> results = customerDynamoDbTable.query(r -> r.queryConditional(keyEqualTo(k -> k.partitionValue("Smith"))));
   results.subscribe(myCustomerResultsProcessor);
   // Perform other work and let the processor handle the results asynchronously.
   ```

   如需使用 的更完整範例`SdkPublisher API`，請參閱討論本指南非同步`scan()`方法一節中[的範例](ddb-en-client-use-multirecord.md#ddb-en-client-use-multirecord-scan-async)。

# 資料類別註釋
<a name="ddb-en-client-anno-index"></a>

下表列出可用於資料類別的註釋，並提供本指南中資訊和範例的連結。資料表會依註釋名稱遞增字母順序排序。


**本指南中使用的資料類別註釋**  

| 註釋名稱 | 註釋適用於 1 | 它的功能 | 本指南中顯示的位置 | 
| --- | --- | --- | --- | 
| DynamoDbAtomicCounter | 屬性 2 | 每次將記錄寫入資料庫時，都會遞增標記的數值屬性。 | [簡介和討論。](ddb-en-client-extensions.md#ddb-en-client-extensions-ACE) | 
| DynamoDbAttribute | 屬性 | 定義或重新命名映射至 DynamoDB 資料表屬性的 Bean 屬性。 |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/sdk-for-java/latest/developer-guide/ddb-en-client-anno-index.html)  | 
| DynamoDbAutoGeneratedTimestampAttribute | 屬性 | 每次項目成功寫入資料庫時，都會以目前時間戳記更新已標記的屬性 | [簡介和討論](ddb-en-client-extensions.md#ddb-en-client-extensions-AGTE)。 | 
| DynamoDbAutoGeneratedUuid | 屬性 | 將新記錄寫入資料庫時，為屬性產生唯一的 UUID （通用唯一識別符）。 | [簡介和討論。](ddb-en-client-extensions.md#ddb-en-client-extensions-AGUE) | 
| DynamoDbBean | class | 將資料類別標示為可映射至資料表結構描述。 | 請先在「開始使用」區段中的[客戶類別](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-anno-bean-cust)上使用 。本指南中會顯示數個用量。 | 
| DynamoDbConvertedBy | 屬性 | 將自訂AttributeConverter與註釋屬性建立關聯。 | [初始討論和範例。](ddb-en-client-adv-features-conversion.md#ddb-en-client-adv-features-conversion-single) | 
| DynamoDbFlatten | 屬性 | 扁平化個別 DynamoDB 資料類別的所有屬性，並將其新增為最上層屬性至讀取和寫入資料庫的記錄。 |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/sdk-for-java/latest/developer-guide/ddb-en-client-anno-index.html)  | 
| DynamoDbIgnore | 屬性 |  導致屬性保持未映射。  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/sdk-for-java/latest/developer-guide/ddb-en-client-anno-index.html)  | 
| DynamoDbIgnoreNulls | 屬性 | 防止儲存巢狀 DynamoDb 物件的 null 屬性。 | [討論和範例。](ddb-en-client-adv-features-ignore-null.md) | 
| DynamoDbImmutable | class |  將不可變資料類別標記為可映射至資料表結構描述。  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/sdk-for-java/latest/developer-guide/ddb-en-client-anno-index.html)  | 
| DynamoDbPartitionKey | 屬性 |  將屬性標記為 DynamoDb 資料表的主要分割區索引鍵 （雜湊索引鍵）。  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/sdk-for-java/latest/developer-guide/ddb-en-client-anno-index.html)  | 
| DynamoDbPreserveEmptyObject | 屬性 |  指定如果對應至註釋屬性的物件沒有資料，則應使用所有 null 欄位初始化物件。  | [討論和範例。](ddb-en-client-adv-features-empty.md) | 
| DynamoDbSecondaryPartitionKey | 屬性 |  將屬性標記為全域次要索引的分割區索引鍵。  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/sdk-for-java/latest/developer-guide/ddb-en-client-anno-index.html)  | 
| DynamoDbSecondarySortKey | 屬性 |  將屬性標記為全域或本機次要索引的選用排序索引鍵。  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/sdk-for-java/latest/developer-guide/ddb-en-client-anno-index.html)  | 
| DynamoDbSortKey | 屬性 |  將屬性標記為選用的主要排序索引鍵 （範圍索引鍵）。  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/sdk-for-java/latest/developer-guide/ddb-en-client-anno-index.html)  | 
| DynamoDbUpdateBehavior | 屬性 |  指定在 UpdateItem 等 'update' 操作中更新此屬性時的行為。  | [簡介和範例。](ddb-en-client-adv-features-upd-behavior.md) | 
| DynamoDbVersionAttribute | 屬性 | 遞增項目版本號碼。 | [簡介和討論。](ddb-en-client-extensions.md#ddb-en-client-extensions-VRE) | 

1您可以將屬性層級註釋套用至 getter 或 setter，但不能同時套用兩者。本指南顯示 getter 上的註釋。

2 該術語`property`通常用於封裝在 JavaBean 資料類別中的值。不過，本指南會`attribute`改用 一詞，以符合 DynamoDB 所使用的術語。

# 使用 Amazon EC2
<a name="examples-ec2"></a>

本節提供[Amazon EC2](https://docs.aws.amazon.com/ec2/)使用 適用於 Java 的 AWS SDK 2.x 的程式設計範例。

**Topics**
+ [管理 Amazon EC2 執行個體](examples-ec2-instances.md)
+ [使用 AWS 區域 和可用區域](examples-ec2-regions-zones.md)
+ [在 中使用安全群組 Amazon EC2](examples-ec2-security-groups.md)
+ [使用 Amazon EC2 執行個體中繼資料](examples-ec2-IMDS.md)

# 管理 Amazon EC2 執行個體
<a name="examples-ec2-instances"></a>

## 建立 執行個體
<a name="create-an-instance"></a>

透過呼叫 [Ec2Client](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/Ec2Client.html) 的 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/Ec2Client.html#runInstances(software.amazon.awssdk.services.ec2.model.RunInstancesRequest)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/Ec2Client.html#runInstances(software.amazon.awssdk.services.ec2.model.RunInstancesRequest))方法建立新的 Amazon EC2 執行個體，提供 [RunInstancesRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/RunInstancesRequest.html)，其中包含要使用的 [Amazon Machine Image (AMI)](https://docs.aws.amazon.com//AWSEC2/latest/UserGuide/AMIs.html) 和[執行個體類型](https://docs.aws.amazon.com//AWSEC2/latest/UserGuide/instance-types.html)。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ec2.Ec2Client;
import software.amazon.awssdk.services.ec2.model.InstanceType;
import software.amazon.awssdk.services.ec2.model.RunInstancesRequest;
import software.amazon.awssdk.services.ec2.model.RunInstancesResponse;
import software.amazon.awssdk.services.ec2.model.Tag;
import software.amazon.awssdk.services.ec2.model.CreateTagsRequest;
import software.amazon.awssdk.services.ec2.model.Ec2Exception;
```

 **Code** 

```
   public static String createEC2Instance(Ec2Client ec2,String name, String amiId ) {

        RunInstancesRequest runRequest = RunInstancesRequest.builder()
                .imageId(amiId)
                .instanceType(InstanceType.T1_MICRO)
                .maxCount(1)
                .minCount(1)
                .build();

        RunInstancesResponse response = ec2.runInstances(runRequest);
        String instanceId = response.instances().get(0).instanceId();

        Tag tag = Tag.builder()
                .key("Name")
                .value(name)
                .build();

        CreateTagsRequest tagRequest = CreateTagsRequest.builder()
                .resources(instanceId)
                .tags(tag)
                .build();

        try {
            ec2.createTags(tagRequest);
            System.out.printf(
                    "Successfully started EC2 Instance %s based on AMI %s",
                    instanceId, amiId);

          return instanceId;

        } catch (Ec2Exception e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }

        return "";
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/ac748d8ef99cd17e297cb74fe13aa671e2679088/javav2/example_code/ec2/src/main/java/com/example/ec2/CreateInstance.java)。

## 啟動執行個體
<a name="start-an-instance"></a>

若要啟動 Amazon EC2 執行個體，請呼叫 Ec2Client 的 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/Ec2Client.html#startInstances(software.amazon.awssdk.services.ec2.model.StartInstancesRequest)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/Ec2Client.html#startInstances(software.amazon.awssdk.services.ec2.model.StartInstancesRequest))方法，為其提供 [StartInstancesRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/StartInstancesRequest.html)，其中包含要啟動的執行個體 ID。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ec2.Ec2Client;
import software.amazon.awssdk.services.ec2.model.StartInstancesRequest;
import software.amazon.awssdk.services.ec2.model.StopInstancesRequest;
```

 **Code** 

```
    public static void startInstance(Ec2Client ec2, String instanceId) {

        StartInstancesRequest request = StartInstancesRequest.builder()
                .instanceIds(instanceId)
                .build();

        ec2.startInstances(request);
        System.out.printf("Successfully started instance %s", instanceId);
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/ac748d8ef99cd17e297cb74fe13aa671e2679088/javav2/example_code/ec2/src/main/java/com/example/ec2/StartStopInstance.java)。

## 停止執行個體
<a name="stop-an-instance"></a>

若要停止 Amazon EC2 執行個體，請呼叫 Ec2Client 的 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/Ec2Client.html#stopInstances(software.amazon.awssdk.services.ec2.model.StopInstancesRequest)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/Ec2Client.html#stopInstances(software.amazon.awssdk.services.ec2.model.StopInstancesRequest))方法，為其提供 [StopInstancesRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/StopInstancesRequest.html)，其中包含要停止的執行個體 ID。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ec2.Ec2Client;
import software.amazon.awssdk.services.ec2.model.StartInstancesRequest;
import software.amazon.awssdk.services.ec2.model.StopInstancesRequest;
```

 **Code** 

```
    public static void stopInstance(Ec2Client ec2, String instanceId) {

        StopInstancesRequest request = StopInstancesRequest.builder()
                .instanceIds(instanceId)
                .build();

        ec2.stopInstances(request);
        System.out.printf("Successfully stopped instance %s", instanceId);
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/ac748d8ef99cd17e297cb74fe13aa671e2679088/javav2/example_code/ec2/src/main/java/com/example/ec2/StartStopInstance.java)。

## 重新啟動執行個體
<a name="reboot-an-instance"></a>

若要重新啟動 Amazon EC2 執行個體，請呼叫 Ec2Client 的 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/Ec2Client.html#rebootInstances(software.amazon.awssdk.services.ec2.model.RebootInstancesRequest)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/Ec2Client.html#rebootInstances(software.amazon.awssdk.services.ec2.model.RebootInstancesRequest))方法，為其提供 [RebootInstancesRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/RebootInstancesRequest.html)，其中包含要重新啟動的執行個體 ID。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ec2.Ec2Client;
import software.amazon.awssdk.services.ec2.model.Ec2Exception;
import software.amazon.awssdk.services.ec2.model.RebootInstancesRequest;
```

 **Code** 

```
    public static void rebootEC2Instance(Ec2Client ec2, String instanceId) {

      try {
            RebootInstancesRequest request = RebootInstancesRequest.builder()
                .instanceIds(instanceId)
                    .build();

            ec2.rebootInstances(request);
            System.out.printf(
                "Successfully rebooted instance %s", instanceId);
    } catch (Ec2Exception e) {
          System.err.println(e.awsErrorDetails().errorMessage());
          System.exit(1);
     }
  }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/ac748d8ef99cd17e297cb74fe13aa671e2679088/javav2/example_code/ec2/src/main/java/com/example/ec2/RebootInstance.java)。

## 描述執行個體
<a name="describe-instances"></a>

若要列出您的執行個體，請建立 [DescribeInstancesRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/DescribeInstancesRequest.html) 並呼叫 Ec2Client 的 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/Ec2Client.html#describeInstances(software.amazon.awssdk.services.ec2.model.DescribeInstancesRequest)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/Ec2Client.html#describeInstances(software.amazon.awssdk.services.ec2.model.DescribeInstancesRequest))方法。它會傳回 [DescribeInstancesResponse](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/DescribeInstancesResponse.html) 物件，您可以用來列出您帳戶和區域的 Amazon EC2 執行個體。

執行個體依照*保留*分組。每個保留對應到呼叫 `startInstances`，用以啟動執行個體。若要列出您的執行個體，您必須先呼叫 `DescribeInstancesResponse` 類別的 `reservations` 方法，然後在每個傳回的 `instances`Reservation[ 物件上呼叫 ](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/Reservation.html)。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ec2.Ec2Client;
import software.amazon.awssdk.services.ec2.model.DescribeInstancesRequest;
import software.amazon.awssdk.services.ec2.model.DescribeInstancesResponse;
import software.amazon.awssdk.services.ec2.model.Instance;
import software.amazon.awssdk.services.ec2.model.Reservation;
import software.amazon.awssdk.services.ec2.model.Ec2Exception;
```

 **Code** 

```
    public static void describeEC2Instances( Ec2Client ec2){

        String nextToken = null;

        try {

            do {
                DescribeInstancesRequest request = DescribeInstancesRequest.builder().maxResults(6).nextToken(nextToken).build();
                DescribeInstancesResponse response = ec2.describeInstances(request);

                for (Reservation reservation : response.reservations()) {
                    for (Instance instance : reservation.instances()) {
                        System.out.println("Instance Id is " + instance.instanceId());
                        System.out.println("Image id is "+  instance.imageId());
                        System.out.println("Instance type is "+  instance.instanceType());
                        System.out.println("Instance state name is "+  instance.state().name());
                        System.out.println("monitoring information is "+  instance.monitoring().state());

                }
            }
                nextToken = response.nextToken();
            } while (nextToken != null);

        } catch (Ec2Exception e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

結果會分頁；您可以將結果物件的 `nextToken` 方法所傳回的值傳遞給新請求物件的 `nextToken` 方法，然後在下一次呼叫 `describeInstances` 時使用新的請求物件，以取得進一步結果。

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/ac748d8ef99cd17e297cb74fe13aa671e2679088/javav2/example_code/ec2/src/main/java/com/example/ec2/DescribeInstances.java)。

## 監控執行個體
<a name="monitor-an-instance"></a>

您可以監控 Amazon EC2 執行個體的各個層面，例如 CPU 和網路使用率、可用的記憶體，以及剩餘的磁碟空間。若要進一步了解執行個體監控，請參閱《Linux 執行個體 Amazon EC2 使用者指南》中的[監控 Amazon EC2](https://docs.aws.amazon.com//AWSEC2/latest/UserGuide/monitoring_ec2.html)。

若要開始監控執行個體，您必須使用要監控的執行個體 ID 建立 [MonitorInstancesRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/MonitorInstancesRequest.html)，並將其傳遞至 Ec2Client 的 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/Ec2Client.html#monitorInstances(software.amazon.awssdk.services.ec2.model.MonitorInstancesRequest)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/Ec2Client.html#monitorInstances(software.amazon.awssdk.services.ec2.model.MonitorInstancesRequest))方法。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ec2.Ec2Client;
import software.amazon.awssdk.services.ec2.model.MonitorInstancesRequest;
import software.amazon.awssdk.services.ec2.model.UnmonitorInstancesRequest;
```

 **Code** 

```
    public static void monitorInstance( Ec2Client ec2, String instanceId) {

        MonitorInstancesRequest request = MonitorInstancesRequest.builder()
                .instanceIds(instanceId).build();

        ec2.monitorInstances(request);
        System.out.printf(
                "Successfully enabled monitoring for instance %s",
                instanceId);
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/ac748d8ef99cd17e297cb74fe13aa671e2679088/javav2/example_code/ec2/src/main/java/com/example/ec2/MonitorInstance.java)。

## 停止監控執行個體
<a name="stop-instance-monitoring"></a>

若要停止監控執行個體，請使用執行個體的 ID 建立 [UnmonitorInstancesRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/UnmonitorInstancesRequest.html) 以停止監控，並將其傳遞至 Ec2Client 的 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/Ec2Client.html#unmonitorInstances(software.amazon.awssdk.services.ec2.model.UnmonitorInstancesRequest)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/Ec2Client.html#unmonitorInstances(software.amazon.awssdk.services.ec2.model.UnmonitorInstancesRequest))方法。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ec2.Ec2Client;
import software.amazon.awssdk.services.ec2.model.MonitorInstancesRequest;
import software.amazon.awssdk.services.ec2.model.UnmonitorInstancesRequest;
```

 **Code** 

```
    public static void unmonitorInstance(Ec2Client ec2, String instanceId) {
        UnmonitorInstancesRequest request = UnmonitorInstancesRequest.builder()
                .instanceIds(instanceId).build();

        ec2.unmonitorInstances(request);

        System.out.printf(
                "Successfully disabled monitoring for instance %s",
                instanceId);
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/ac748d8ef99cd17e297cb74fe13aa671e2679088/javav2/example_code/ec2/src/main/java/com/example/ec2/MonitorInstance.java)。

## 其他資訊
<a name="more-information"></a>
+  Amazon EC2 API 參考中的 [RunInstances](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_RunInstances.html) 
+  Amazon EC2 API 參考中的 [DescribeInstances](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeInstances.html) 
+  Amazon EC2 API 參考中的 [StartInstances](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_StartInstances.html) 
+  Amazon EC2 API 參考中的 [StopInstances](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_StopInstances.html) 
+  API Amazon EC2 參考中的 [RebootInstances](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_RebootInstances.html) 
+  Amazon EC2 API 參考中的 [MonitorInstances](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_MonitorInstances.html) 
+  Amazon EC2 API 參考中的 [UnmonitorInstances](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_UnmonitorInstances.html) 

# 使用 AWS 區域 和可用區域
<a name="examples-ec2-regions-zones"></a>

## 描述區域
<a name="describe-regions"></a>

若要列出您的帳戶可用的區域，請呼叫 Ec2Client 的 `describeRegions`方法。它會傳回 [DescribeRegionsResponse](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/DescribeRegionsResponse.html)。呼叫傳回物件的 `regions` 方法以取得代表每個區域的 [Region](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/Region.html) 物件清單。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ec2.Ec2AsyncClient;
import software.amazon.awssdk.services.ec2.model.DescribeRegionsResponse;
import software.amazon.awssdk.services.ec2.model.DescribeAvailabilityZonesResponse;
import java.util.concurrent.CompletableFuture;
```

 **Code** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ec2.Ec2AsyncClient;
import software.amazon.awssdk.services.ec2.model.DescribeRegionsResponse;
import software.amazon.awssdk.services.ec2.model.DescribeAvailabilityZonesResponse;
import java.util.concurrent.CompletableFuture;

/**
 * Before running this Java V2 code example, set up your development
 * environment, including your credentials.
 *
 * For more information, see the following documentation topic:
 *
 * https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html
 */
public class DescribeRegionsAndZones {
    public static void main(String[] args) {
        Ec2AsyncClient ec2AsyncClient = Ec2AsyncClient.builder()
            .region(Region.US_EAST_1)
            .build();

        try {
            CompletableFuture<Void> future = describeEC2RegionsAndZonesAsync(ec2AsyncClient);
            future.join(); // Wait for both async operations to complete.
        } catch (RuntimeException rte) {
            System.err.println("An exception occurred: " + (rte.getCause() != null ? rte.getCause().getMessage() : rte.getMessage()));
        }
    }

    /**
     * Asynchronously describes the EC2 regions and availability zones.
     *
     * @param ec2AsyncClient the EC2 async client used to make the API calls
     * @return a {@link CompletableFuture} that completes when both the region and availability zone descriptions are complete
     */
    public static CompletableFuture<Void> describeEC2RegionsAndZonesAsync(Ec2AsyncClient ec2AsyncClient) {
        // Initiate the asynchronous request to describe regions
        CompletableFuture<DescribeRegionsResponse> regionsResponse = ec2AsyncClient.describeRegions();

        // Handle the response or exception for regions
        CompletableFuture<DescribeRegionsResponse> regionsFuture = regionsResponse.whenComplete((regionsResp, ex) -> {
            if (ex != null) {
                // Handle the exception by throwing a RuntimeException
                throw new RuntimeException("Failed to describe EC2 regions.", ex);
            } else if (regionsResp == null || regionsResp.regions().isEmpty()) {
                // Throw an exception if the response is null or the result is empty
                throw new RuntimeException("No EC2 regions found.");
            } else {
                // Process the response if no exception occurred and the result is not empty
                regionsResp.regions().forEach(region -> {
                    System.out.printf(
                        "Found Region %s with endpoint %s%n",
                        region.regionName(),
                        region.endpoint());
                });
            }
        });

        CompletableFuture<DescribeAvailabilityZonesResponse> zonesResponse = ec2AsyncClient.describeAvailabilityZones();
        CompletableFuture<DescribeAvailabilityZonesResponse> zonesFuture = zonesResponse.whenComplete((zonesResp, ex) -> {
            if (ex != null) {
                throw new RuntimeException("Failed to describe EC2 availability zones.", ex);
            } else if (zonesResp == null || zonesResp.availabilityZones().isEmpty()) {
                throw new RuntimeException("No EC2 availability zones found.");
            } else {
                zonesResp.availabilityZones().forEach(zone -> {
                    System.out.printf(
                        "Found Availability Zone %s with status %s in region %s%n",
                        zone.zoneName(),
                        zone.state(),
                        zone.regionName()
                    );
                });
            }
        });

        return CompletableFuture.allOf(regionsFuture, zonesFuture);
    }
}
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/7486a1a092aa8e16a21698ef26f9d524fef62e55/javav2/example_code/ec2/src/main/java/com/example/ec2/DescribeRegionsAndZones.java)。

## 描述可用區域
<a name="describe-availability-zones"></a>

若要列出您帳戶可用的每個可用區域，請呼叫 Ec2Client 的 `describeAvailabilityZones`方法。它會傳回 [DescribeAvailabilityZonesResponse](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/DescribeAvailabilityZonesResponse.html)。呼叫其 `availabilityZones` 方法以取得代表每個可用區域的 [AvailabilityZone](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/AvailabilityZone.html) 物件清單。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ec2.Ec2AsyncClient;
import software.amazon.awssdk.services.ec2.model.DescribeRegionsResponse;
import software.amazon.awssdk.services.ec2.model.DescribeAvailabilityZonesResponse;
import java.util.concurrent.CompletableFuture;
```

 **Code** 

建立 Ec2Client。

```
        Ec2AsyncClient ec2AsyncClient = Ec2AsyncClient.builder()
            .region(Region.US_EAST_1)
            .build();
```

然後呼叫 describeAvailabilityZones () 並擷取結果。

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ec2.Ec2AsyncClient;
import software.amazon.awssdk.services.ec2.model.DescribeRegionsResponse;
import software.amazon.awssdk.services.ec2.model.DescribeAvailabilityZonesResponse;
import java.util.concurrent.CompletableFuture;

/**
 * Before running this Java V2 code example, set up your development
 * environment, including your credentials.
 *
 * For more information, see the following documentation topic:
 *
 * https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html
 */
public class DescribeRegionsAndZones {
    public static void main(String[] args) {
        Ec2AsyncClient ec2AsyncClient = Ec2AsyncClient.builder()
            .region(Region.US_EAST_1)
            .build();

        try {
            CompletableFuture<Void> future = describeEC2RegionsAndZonesAsync(ec2AsyncClient);
            future.join(); // Wait for both async operations to complete.
        } catch (RuntimeException rte) {
            System.err.println("An exception occurred: " + (rte.getCause() != null ? rte.getCause().getMessage() : rte.getMessage()));
        }
    }

    /**
     * Asynchronously describes the EC2 regions and availability zones.
     *
     * @param ec2AsyncClient the EC2 async client used to make the API calls
     * @return a {@link CompletableFuture} that completes when both the region and availability zone descriptions are complete
     */
    public static CompletableFuture<Void> describeEC2RegionsAndZonesAsync(Ec2AsyncClient ec2AsyncClient) {
        // Initiate the asynchronous request to describe regions
        CompletableFuture<DescribeRegionsResponse> regionsResponse = ec2AsyncClient.describeRegions();

        // Handle the response or exception for regions
        CompletableFuture<DescribeRegionsResponse> regionsFuture = regionsResponse.whenComplete((regionsResp, ex) -> {
            if (ex != null) {
                // Handle the exception by throwing a RuntimeException
                throw new RuntimeException("Failed to describe EC2 regions.", ex);
            } else if (regionsResp == null || regionsResp.regions().isEmpty()) {
                // Throw an exception if the response is null or the result is empty
                throw new RuntimeException("No EC2 regions found.");
            } else {
                // Process the response if no exception occurred and the result is not empty
                regionsResp.regions().forEach(region -> {
                    System.out.printf(
                        "Found Region %s with endpoint %s%n",
                        region.regionName(),
                        region.endpoint());
                });
            }
        });

        CompletableFuture<DescribeAvailabilityZonesResponse> zonesResponse = ec2AsyncClient.describeAvailabilityZones();
        CompletableFuture<DescribeAvailabilityZonesResponse> zonesFuture = zonesResponse.whenComplete((zonesResp, ex) -> {
            if (ex != null) {
                throw new RuntimeException("Failed to describe EC2 availability zones.", ex);
            } else if (zonesResp == null || zonesResp.availabilityZones().isEmpty()) {
                throw new RuntimeException("No EC2 availability zones found.");
            } else {
                zonesResp.availabilityZones().forEach(zone -> {
                    System.out.printf(
                        "Found Availability Zone %s with status %s in region %s%n",
                        zone.zoneName(),
                        zone.state(),
                        zone.regionName()
                    );
                });
            }
        });

        return CompletableFuture.allOf(regionsFuture, zonesFuture);
    }
}
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/7486a1a092aa8e16a21698ef26f9d524fef62e55/javav2/example_code/ec2/src/main/java/com/example/ec2/DescribeRegionsAndZones.java)。

## 描述帳戶
<a name="describe-accounts"></a>

若要列出您帳戶的 EC2-related，請呼叫 Ec2Client 的 `describeAccountAttributes`方法。此方法會傳回一個 [DescribeAccountAttributesResponse](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/DescribeAccountAttributesResponse.html) 物件。叫用此物件的 `accountAttributes` 方法來取得 [AccountAttribute](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/AccountAttribute.html) 物件的清單。您可以逐一查看清單以擷取`AccountAttribute`物件。

您可以透過叫用`AccountAttribute`物件的 `attributeValues`方法，取得您帳戶的屬性值。此方法會傳回 [AccountAttributeValue](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/AccountAttributeValue.html) 物件的清單。您可以逐一查看第二個清單以顯示屬性值 (請參閱下列程式碼範例)。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ec2.Ec2AsyncClient;
import software.amazon.awssdk.services.ec2.model.DescribeAccountAttributesResponse;
import java.util.concurrent.CompletableFuture;
```

 **Code** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ec2.Ec2AsyncClient;
import software.amazon.awssdk.services.ec2.model.DescribeAccountAttributesResponse;
import java.util.concurrent.CompletableFuture;

/**
 * Before running this Java V2 code example, set up your development
 * environment, including your credentials.
 *
 * For more information, see the following documentation topic:
 *
 * https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html
 */
public class DescribeAccount {
    public static void main(String[] args) {
        Ec2AsyncClient ec2AsyncClient = Ec2AsyncClient.builder()
            .region(Region.US_EAST_1)
            .build();

        try {
            CompletableFuture<DescribeAccountAttributesResponse> future = describeEC2AccountAsync(ec2AsyncClient);
            future.join();
            System.out.println("EC2 Account attributes described successfully.");
        } catch (RuntimeException rte) {
            System.err.println("An exception occurred: " + (rte.getCause() != null ? rte.getCause().getMessage() : rte.getMessage()));
        }
    }

    /**
     * Describes the EC2 account attributes asynchronously.
     *
     * @param ec2AsyncClient the EC2 asynchronous client to use for the operation
     * @return a {@link CompletableFuture} containing the {@link DescribeAccountAttributesResponse} with the account attributes
     */
    public static CompletableFuture<DescribeAccountAttributesResponse> describeEC2AccountAsync(Ec2AsyncClient ec2AsyncClient) {
        CompletableFuture<DescribeAccountAttributesResponse> response = ec2AsyncClient.describeAccountAttributes();
        return response.whenComplete((accountResults, ex) -> {
            if (ex != null) {
                // Handle the exception by throwing a RuntimeException.
                throw new RuntimeException("Failed to describe EC2 account attributes.", ex);
            } else if (accountResults == null || accountResults.accountAttributes().isEmpty()) {
                // Throw an exception if the response is null or no account attributes are found.
                throw new RuntimeException("No account attributes found.");
            } else {
                // Process the response if no exception occurred.
                accountResults.accountAttributes().forEach(attribute -> {
                    System.out.println("\nThe name of the attribute is " + attribute.attributeName());
                    attribute.attributeValues().forEach(
                        myValue -> System.out.println("The value of the attribute is " + myValue.attributeValue()));
                });
            }
        });
    }
}
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/7486a1a092aa8e16a21698ef26f9d524fef62e55/javav2/example_code/ec2/src/main/java/com/example/ec2/DescribeAccount.java)。

## 其他資訊
<a name="more-information"></a>
+  Linux 執行個體 Amazon EC2 使用者指南中的[區域和可用區域](https://docs.aws.amazon.com//AWSEC2/latest/UserGuide/using-regions-availability-zones.html) 
+  Amazon EC2 API 參考中的 [DescribeRegions](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeRegions.html) 
+  Amazon EC2 API 參考中的 [DescribeAvailabilityZones](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeAvailabilityZones.html) 

# 在 中使用安全群組 Amazon EC2
<a name="examples-ec2-security-groups"></a>

## 建立安全群組
<a name="create-a-security-group"></a>

若要建立安全群組，請使用包含金鑰名稱的 [CreateSecurityGroupRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/CreateSecurityGroupRequest.html) 呼叫 Ec2Client 的 `createSecurityGroup`方法。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ec2.Ec2Client;
import software.amazon.awssdk.services.ec2.model.CreateSecurityGroupRequest;
import software.amazon.awssdk.services.ec2.model.AuthorizeSecurityGroupIngressRequest;
import software.amazon.awssdk.services.ec2.model.AuthorizeSecurityGroupIngressResponse;
import software.amazon.awssdk.services.ec2.model.Ec2Exception;
import software.amazon.awssdk.services.ec2.model.IpPermission;
import software.amazon.awssdk.services.ec2.model.CreateSecurityGroupResponse;
import software.amazon.awssdk.services.ec2.model.IpRange;
```

 **Code** 

```
            CreateSecurityGroupRequest createRequest = CreateSecurityGroupRequest.builder()
                .groupName(groupName)
                .description(groupDesc)
                .vpcId(vpcId)
                .build();

            CreateSecurityGroupResponse resp= ec2.createSecurityGroup(createRequest);
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/ec2/src/main/java/com/example/ec2/CreateSecurityGroup.java)。

## 設定安全群組
<a name="configure-a-security-group"></a>

安全群組可以控制執行個體的傳入 （傳入） 和傳出 （傳出） 流量 Amazon EC2 。

若要將輸入規則新增至您的安全群組，請使用 Ec2Client 的 `authorizeSecurityGroupIngress`方法，提供安全群組的名稱，以及您想要在 [AuthorizeSecurityGroupIngressRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/AuthorizeSecurityGroupIngressRequest.html) 物件中為其指派的存取規則 ([IpPermission](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/IpPermission.html))。以下範例說明如何將 IP 許可新增至安全群組。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ec2.Ec2Client;
import software.amazon.awssdk.services.ec2.model.CreateSecurityGroupRequest;
import software.amazon.awssdk.services.ec2.model.AuthorizeSecurityGroupIngressRequest;
import software.amazon.awssdk.services.ec2.model.AuthorizeSecurityGroupIngressResponse;
import software.amazon.awssdk.services.ec2.model.Ec2Exception;
import software.amazon.awssdk.services.ec2.model.IpPermission;
import software.amazon.awssdk.services.ec2.model.CreateSecurityGroupResponse;
import software.amazon.awssdk.services.ec2.model.IpRange;
```

 **Code** 

首先，建立 Ec2Client

```
        Region region = Region.US_WEST_2;
        Ec2Client ec2 = Ec2Client.builder()
                .region(region)
                .build();
```

然後使用 Ec2Client 的 `authorizeSecurityGroupIngress`方法，

```
            IpRange ipRange = IpRange.builder()
                .cidrIp("0.0.0.0/0").build();

            IpPermission ipPerm = IpPermission.builder()
                .ipProtocol("tcp")
                .toPort(80)
                .fromPort(80)
                .ipRanges(ipRange)
                .build();

            IpPermission ipPerm2 = IpPermission.builder()
                .ipProtocol("tcp")
                .toPort(22)
                .fromPort(22)
                .ipRanges(ipRange)
                .build();

            AuthorizeSecurityGroupIngressRequest authRequest =
                AuthorizeSecurityGroupIngressRequest.builder()
                        .groupName(groupName)
                        .ipPermissions(ipPerm, ipPerm2)
                        .build();

            AuthorizeSecurityGroupIngressResponse authResponse =
            ec2.authorizeSecurityGroupIngress(authRequest);

            System.out.printf(
                "Successfully added ingress policy to Security Group %s",
                groupName);

            return resp.groupId();

        } catch (Ec2Exception e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
        return "";
    }
```

若要將輸出規則新增至安全群組，請在 [AuthorizeSecurityGroupEgressRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/AuthorizeSecurityGroupEgressRequest.html) 中提供類似的資料給 Ec2Client 的 `authorizeSecurityGroupEgress`方法。

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/ec2/src/main/java/com/example/ec2/CreateSecurityGroup.java)。

## 描述安全群組
<a name="describe-security-groups"></a>

若要描述您的安全群組或取得相關資訊，請呼叫 Ec2Client 的 `describeSecurityGroups`方法。它會傳回 [DescribeSecurityGroupsResponse](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/DescribeSecurityGroupsResponse.html)，您可透過呼叫它的 `securityGroups` 方法 (此方法會傳回 [SecurityGroup](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/SecurityGroup.html) 物件清單)，來存取安全群組的清單。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ec2.Ec2Client;
import software.amazon.awssdk.services.ec2.model.DescribeSecurityGroupsRequest;
import software.amazon.awssdk.services.ec2.model.DescribeSecurityGroupsResponse;
import software.amazon.awssdk.services.ec2.model.SecurityGroup;
import software.amazon.awssdk.services.ec2.model.Ec2Exception;
```

 **Code** 

```
     public static void describeEC2SecurityGroups(Ec2Client ec2, String groupId) {

        try {
            DescribeSecurityGroupsRequest request =
                DescribeSecurityGroupsRequest.builder()
                        .groupIds(groupId).build();

            DescribeSecurityGroupsResponse response =
                ec2.describeSecurityGroups(request);

             for(SecurityGroup group : response.securityGroups()) {
                System.out.printf(
                    "Found Security Group with id %s, " +
                            "vpc id %s " +
                            "and description %s",
                    group.groupId(),
                    group.vpcId(),
                    group.description());
            }
        } catch (Ec2Exception e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/ec2/src/main/java/com/example/ec2/DescribeSecurityGroups.java)。

## 刪除安全群組
<a name="delete-a-security-group"></a>

若要刪除安全群組，請呼叫 Ec2Client 的 `deleteSecurityGroup`方法，並向其傳遞 [DeleteSecurityGroupRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/DeleteSecurityGroupRequest.html)，其中包含要刪除的安全群組 ID。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ec2.Ec2Client;
import software.amazon.awssdk.services.ec2.model.DeleteSecurityGroupRequest;
import software.amazon.awssdk.services.ec2.model.Ec2Exception;
```

 **Code** 

```
    public static void deleteEC2SecGroup(Ec2Client ec2,String groupId) {

        try {
            DeleteSecurityGroupRequest request = DeleteSecurityGroupRequest.builder()
                .groupId(groupId)
                .build();

            ec2.deleteSecurityGroup(request);
            System.out.printf(
                "Successfully deleted Security Group with id %s", groupId);

        } catch (Ec2Exception e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
     }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/ec2/src/main/java/com/example/ec2/DeleteSecurityGroup.java)。

## 其他資訊
<a name="more-information"></a>
+  Linux 執行個體 Amazon EC2 使用者指南中的[Amazon EC2 安全群組](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-security-groups.html) 
+  《[Linux 執行個體使用者指南》中的授權 Linux 執行個體的傳入流量](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/authorizing-access-to-an-instance.html) Amazon EC2 
+  Amazon EC2 API 參考中的 [CreateSecurityGroup](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_CreateSecurityGroup.html) 
+  Amazon EC2 API 參考中的 [DescribeSecurityGroups](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSecurityGroups.html) 
+  Amazon EC2 API 參考中的 [DeleteSecurityGroup](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DeleteSecurityGroup.html) 
+  Amazon EC2 API 參考中的 [AuthorizeSecurityGroupIngress](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_AuthorizeSecurityGroupIngress.html) 

# 使用 Amazon EC2 執行個體中繼資料
<a name="examples-ec2-IMDS"></a>

Amazon EC2 執行個體中繼資料服務的 Java SDK 用戶端 （中繼資料用戶端） 可讓您的應用程式存取其本機 EC2 執行個體上的中繼資料。中繼資料用戶端使用 [IMDSv2](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html) (Instance Metadata Service v2) 的本機執行個體，並使用工作階段導向請求。

開發套件提供兩種用戶端類別。同步`[Ec2MetadataClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/imds/Ec2MetadataClient.html)`用於封鎖操作，而 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/imds/Ec2MetadataAsyncClient.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/imds/Ec2MetadataAsyncClient.html)用於非同步、非封鎖的使用案例。

## 開始使用
<a name="examples-ec2-IMDS-getstarted"></a>

若要使用中繼資料用戶端，請將 `imds` Maven 成品新增至您的專案。您也需要 classpath 上 `[SdkHttpClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/SdkHttpClient.html)`（或非同步變體`[SdkAsyncHttpClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/async/SdkAsyncHttpClient.html)`的 ) 的類別。

下列 Maven XML 顯示使用同步 [UrlConnectionHttpClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/urlconnection/UrlConnectionHttpClient.html) 以及中繼資料用戶端相依性的相依性程式碼片段。

```
<dependencyManagement>
   <dependencies>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>bom</artifactId>
            <version>VERSION</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>imds</artifactId>
    </dependency>
    <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>url-connection-client</artifactId>
    </dependency>
    <!-- other dependencies --> 
</dependencies>
```

搜尋 [Maven 中央儲存庫](https://central.sonatype.com/artifact/software.amazon.awssdk/bom)以取得最新版本的`bom`成品。

若要使用非同步 HTTP 用戶端，請取代`url-connection-client`成品的相依性程式碼片段。例如，以下程式碼片段引入 [NettyNioAsyncHttpClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClient.html) 實作。

```
    <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>netty-nio-client</artifactId>
    </dependency>
```

## 使用中繼資料用戶端
<a name="examples-ec2-IMDS-use"></a>

### 執行個體化中繼資料用戶端
<a name="examples-ec2-IMDS-use-create"></a>

當 classpath 上只有一個`SdkHttpClient`介面實作`Ec2MetadataClient`時，您可以執行個體化同步的執行個體。若要這麼做，請呼叫靜態`Ec2MetadataClient#create()`方法，如下列程式碼片段所示。

```
Ec2MetadataClient client = Ec2MetadataClient.create(); // 'Ec2MetadataAsyncClient#create' is the asynchronous version.
```

如果您的應用程式有多個 `SdkHttpClient`或 `SdkHttpAsyncClient` 介面實作，您必須指定中繼資料用戶端要使用的實作，如 [可設定的 HTTP 用戶端](#examples-ec2-IMDS-features-http)一節所示。

**注意**  
對於大多數服務用戶端，例如 Amazon S3，適用於 Java 的 SDK 會自動新增 `SdkHttpClient`或 `SdkHttpAsyncClient` 介面的實作。如果您的中繼資料用戶端使用相同的實作，則 `Ec2MetadataClient#create()`會運作。如果您需要不同的實作，您必須在建立中繼資料用戶端時指定它。

### 傳送請求
<a name="examples-ec2-IMDS-use-req"></a>

若要擷取執行個體中繼資料，請執行個體化 `EC2MetadataClient`類別，並使用指定[執行個體中繼資料類別](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-categories.html)的路徑參數呼叫 `get` 方法。

下列範例會將與 `ami-id`金鑰相關聯的值列印至 主控台。

```
Ec2MetadataClient client = Ec2MetadataClient.create();
Ec2MetadataResponse response = client.get("/latest/meta-data/ami-id");
System.out.println(response.asString());
client.close(); // Closes the internal resources used by the Ec2MetadataClient class.
```

如果路徑無效，`get`方法會擲回例外狀況。

針對多個請求重複使用相同的用戶端執行個體，但在不再需要釋放資源時，在用戶端`close`上呼叫 。呼叫關閉方法之後，就無法再使用用戶端執行個體。

### 剖析回應
<a name="examples-ec2-IMDS-use-pares"></a>

EC2 執行個體中繼資料可以以不同的格式輸出。純文字和 JSON 是最常用的格式。中繼資料用戶端提供使用這些格式的方法。

如下列範例所示，使用 `asString`方法取得資料做為 Java 字串。您也可以使用 `asList`方法來分隔傳回多行的純文字回應。

```
Ec2MetadataClient client = Ec2MetadataClient.create();
Ec2MetadataResponse response = client.get("/latest/meta-data/");
String fullResponse = response.asString();
List<String> splits = response.asList();
```

如果回應為 JSON，請使用 `Ec2MetadataResponse#asDocument`方法將 JSON 回應剖析為[文件](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/document/Document.html)執行個體，如下列程式碼片段所示。

```
Document fullResponse = response.asDocument();
```

如果中繼資料的格式不是 JSON，則會擲回例外狀況。如果成功剖析回應，您可以使用 [文件 API](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/document/package-summary.html) 來更詳細地檢查回應。請參閱執行個體[中繼資料類別圖表](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-categories.html)，以了解哪些中繼資料類別提供 JSON 格式的回應。

## 設定中繼資料用戶端
<a name="examples-ec2-IMDS-config"></a>

### 重試
<a name="examples-ec2-IMDS-config-retries"></a>

您可以使用重試機制設定中繼資料用戶端。如果您這麼做，則用戶端可以自動重試因意外原因失敗的請求。根據預設，用戶端會在失敗的請求上重試三次，並在嘗試之間有指數退避時間。

如果您的使用案例需要不同的重試機制，您可以在其建置器上使用 `retryPolicy`方法自訂用戶端。例如，下列範例顯示同步用戶端，其設定在嘗試與五次重試嘗試之間的固定延遲為兩秒。

```
BackoffStrategy fixedBackoffStrategy = FixedDelayBackoffStrategy.create(Duration.ofSeconds(2));
Ec2MetadataClient client =
    Ec2MetadataClient.builder()
                     .retryPolicy(retryPolicyBuilder -> retryPolicyBuilder.numRetries(5)
                                                                           .backoffStrategy(fixedBackoffStrategy))
                     .build();
```

您可以搭配中繼資料用戶端使用數個 [BackoffStrategies](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/backoff/package-summary.html)。

您也可以完全停用重試機制，如下列程式碼片段所示。

```
Ec2MetadataClient client =
    Ec2MetadataClient.builder()
                    .retryPolicy(Ec2MetadataRetryPolicy.none())
                    .build();
```

使用 會`Ec2MetadataRetryPolicy#none()`停用預設重試政策，讓中繼資料用戶端不會重試。

### IP 版本
<a name="examples-ec2-IMDS-config-ipversion"></a>

根據預設，中繼資料用戶端會使用位於 的 IPV4 端點`http://169.254.169.254`。若要將用戶端變更為使用 IPV6 版本，請使用建置器的 `endpointMode`或 `endpoint`方法。如果在建置器上呼叫這兩種方法，就會產生例外狀況。

下列範例顯示兩個 IPV6 選項。

```
Ec2MetadataClient client =
    Ec2MetadataClient.builder()
                     .endpointMode(EndpointMode.IPV6)
                     .build();
```

```
Ec2MetadataClient client =
    Ec2MetadataClient.builder()
                     .endpoint(URI.create("http://[fd00:ec2::254]"))
                     .build();
```

## 主要功能
<a name="examples-ec2-IMDS-features"></a>

### 非同步用戶端
<a name="examples-ec2-IMDS-features-async"></a>

若要使用用戶端的非封鎖版本，請執行個體化 `Ec2MetadataAsyncClient`類別的執行個體。下列範例中的程式碼會建立具有預設設定的非同步用戶端，並使用 `get`方法擷取`ami-id`金鑰的值。

```
Ec2MetadataAsyncClient asyncClient = Ec2MetadataAsyncClient.create();
CompletableFuture<Ec2MetadataResponse> response = asyncClient.get("/latest/meta-data/ami-id");
```

`get` 方法`java.util.concurrent.CompletableFuture`傳回的 會在回應傳回時完成。下列範例會將`ami-id`中繼資料列印到 主控台。

```
response.thenAccept(metadata -> System.out.println(metadata.asString()));
```

### 可設定的 HTTP 用戶端
<a name="examples-ec2-IMDS-features-http"></a>

每個中繼資料用戶端的建置器都有一種`httpClient`方法，您可以用來提供自訂的 HTTP 用戶端。

下列範例顯示自訂`UrlConnectionHttpClient`執行個體的程式碼。

```
SdkHttpClient httpClient =
    UrlConnectionHttpClient.builder()
                           .socketTimeout(Duration.ofMinutes(5))
                           .proxyConfiguration(proxy -> proxy.endpoint(URI.create("http://proxy.example.net:8888"))))
                           .build();
Ec2MetadataClient metaDataClient =
    Ec2MetadataClient.builder()
                     .httpClient(httpClient)
                     .build();
// Use the metaDataClient instance.
metaDataClient.close();   // Close the instance when no longer needed.
```

下列範例顯示具有非同步中繼資料用戶端之自訂`NettyNioAsyncHttpClient`執行個體的程式碼。

```
SdkAsyncHttpClient httpAsyncClient = 
    NettyNioAsyncHttpClient.builder()
                           .connectionTimeout(Duration.ofMinutes(5))
                           .maxConcurrency(100)
                           .build();
Ec2MetadataAsyncClient asyncMetaDataClient =
    Ec2MetadataAsyncClient.builder()
                          .httpClient(httpAsyncClient)
                          .build();
// Use the asyncMetaDataClient instance.
asyncMetaDataClient.close();   // Close the instance when no longer needed.
```

本指南中的 [在 中設定 HTTP 用戶端 AWS SDK for Java 2.x](http-configuration.md)主題提供如何設定適用於 Java 的 開發套件中可用 HTTP 用戶端的詳細資訊。

### 權杖快取
<a name="examples-ec2-IMDS-features-token"></a>

由於中繼資料用戶端使用 IMDSv2，所有請求都會與工作階段相關聯。工作階段是由具有過期的字符定義，中繼資料用戶端會為您管理。每個中繼資料請求都會自動重複使用字符，直到過期為止。

根據預設，字符會持續六個小時 (21，600 秒）。建議您保留預設time-to-live值，除非您的特定使用案例需要進階組態。

如有需要，請使用`tokenTtl`建置器方法來設定持續時間。例如，以下程式碼片段中的程式碼會建立工作階段持續時間為五分鐘的用戶端。

```
Ec2MetadataClient client =
    Ec2MetadataClient.builder()
                     .tokenTtl(Duration.ofMinutes(5))
                     .build();
```

如果您省略在建置器上呼叫 `tokenTtl`方法，則會改用預設持續時間 21，600。

# 使用 IAM
<a name="examples-iam"></a>

本節提供使用 適用於 Java 的 AWS SDK 2.x 進行程式設計 AWS Identity and Access Management (IAM) 的範例。

 AWS Identity and Access Management (IAM) 可讓您安全地控制使用者對 AWS 服務和資源的存取。使用 IAM，您可以建立和管理 AWS 使用者和群組，並使用許可來允許和拒絕其對 AWS 資源的存取。如需 的完整指南 IAM，請造訪 [IAM 使用者指南](https://docs.aws.amazon.com//IAM/latest/UserGuide/introduction.html)。

下列範例僅包含示範每個技術所需的程式碼。[GitHub 上提供完整程式碼範例](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2)。您可以從那裡下載單一原始檔案或將儲存庫複製到本機，以取得建置和執行的所有範例。

**Topics**
+ [管理 IAM 存取金鑰](examples-iam-access-keys.md)
+ [管理 IAM 使用者](examples-iam-users.md)
+ [建立 IAM 政策](feature-iam-policy-builder.md)
+ [使用 IAM 政策](examples-iam-policies.md)
+ [使用 IAM 伺服器憑證](examples-iam-server-certificates.md)

# 管理 IAM 存取金鑰
<a name="examples-iam-access-keys"></a>

## 建立存取金鑰
<a name="create-an-access-key"></a>

若要建立 IAM 存取金鑰，請使用 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/CreateAccessKeyRequest.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/CreateAccessKeyRequest.html) 物件呼叫 `IamClient’s``createAccessKey`方法。

**注意**  
您必須將區域設定為 **AWS\$1GLOBAL**，`IamClient`呼叫才能運作，因為 IAM 是全域服務。

 **匯入** 

```
import software.amazon.awssdk.services.iam.model.CreateAccessKeyRequest;
import software.amazon.awssdk.services.iam.model.CreateAccessKeyResponse;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.iam.IamClient;
import software.amazon.awssdk.services.iam.model.IamException;
```

 **Code** 

```
    public static String createIAMAccessKey(IamClient iam,String user) {

        try {
            CreateAccessKeyRequest request = CreateAccessKeyRequest.builder()
                .userName(user).build();

            CreateAccessKeyResponse response = iam.createAccessKey(request);
           String keyId = response.accessKey().accessKeyId();
           return keyId;

        } catch (IamException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
        return "";
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f807d60010caf3d14fe4cd0801b842fb8e9511ca/javav2/example_code/iam/src/main/java/com/example/iam/CreateAccessKey.java)。

## 列出存取金鑰
<a name="list-access-keys"></a>

若要列出指定使用者的存取金鑰，請建立包含要列出金鑰之使用者名稱的[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/ListAccessKeysRequest.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/ListAccessKeysRequest.html)物件，並將其傳遞給 `IamClient’s``listAccessKeys`方法。

**注意**  
如果您未提供使用者名稱給 `listAccessKeys`，則會嘗試列出與簽署請求 AWS 帳戶 之 相關聯的存取金鑰。

 **匯入** 

```
import software.amazon.awssdk.services.iam.model.AccessKeyMetadata;
import software.amazon.awssdk.services.iam.model.IamException;
import software.amazon.awssdk.services.iam.model.ListAccessKeysRequest;
import software.amazon.awssdk.services.iam.model.ListAccessKeysResponse;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.iam.IamClient;
```

 **Code** 

```
    public static void listKeys( IamClient iam,String userName ){

        try {
            boolean done = false;
            String newMarker = null;

            while (!done) {
                ListAccessKeysResponse response;

            if(newMarker == null) {
                ListAccessKeysRequest request = ListAccessKeysRequest.builder()
                        .userName(userName).build();
                response = iam.listAccessKeys(request);
            } else {
                ListAccessKeysRequest request = ListAccessKeysRequest.builder()
                        .userName(userName)
                        .marker(newMarker).build();
                response = iam.listAccessKeys(request);
            }

            for (AccessKeyMetadata metadata :
                    response.accessKeyMetadata()) {
                System.out.format("Retrieved access key %s",
                        metadata.accessKeyId());
            }

            if (!response.isTruncated()) {
                done = true;
            } else {
                newMarker = response.marker();
            }
        }

        } catch (IamException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

`listAccessKeys` 的結果會分頁 (每個呼叫預設最多 100 個記錄)。您可以在傳回的[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/ListAccessKeysResponse.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/ListAccessKeysResponse.html)物件`isTruncated`上呼叫 ，以查看查詢是否傳回較少的結果，然後可用。若是如此，請在 `marker` 上呼叫 `ListAccessKeysResponse`，並將它用於建立新的請求。在下次呼叫 `listAccessKeys` 時使用該新的請求。

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f807d60010caf3d14fe4cd0801b842fb8e9511ca/javav2/example_code/iam/src/main/java/com/example/iam/ListAccessKeys.java)。

## 擷取上次使用存取金鑰的時間
<a name="retrieve-an-access-key-s-last-used-time"></a>

若要取得上次使用存取金鑰的時間，請使用存取金鑰的 ID 呼叫 `IamClient’s``getAccessKeyLastUsed`方法 （可以使用 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/GetAccessKeyLastUsedRequest.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/GetAccessKeyLastUsedRequest.html) 物件傳入。

然後，您可以使用傳回的[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/GetAccessKeyLastUsedResponse.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/GetAccessKeyLastUsedResponse.html)物件來擷取金鑰的上次使用時間。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.iam.IamClient;
import software.amazon.awssdk.services.iam.model.GetAccessKeyLastUsedRequest;
import software.amazon.awssdk.services.iam.model.GetAccessKeyLastUsedResponse;
import software.amazon.awssdk.services.iam.model.IamException;
```

 **Code** 

```
    public static void getAccessKeyLastUsed(IamClient iam, String accessId ){

        try {
            GetAccessKeyLastUsedRequest request = GetAccessKeyLastUsedRequest.builder()
                    .accessKeyId(accessId).build();

            GetAccessKeyLastUsedResponse response = iam.getAccessKeyLastUsed(request);

            System.out.println("Access key was last used at: " +
                    response.accessKeyLastUsed().lastUsedDate());

        } catch (IamException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
        System.out.println("Done");
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f807d60010caf3d14fe4cd0801b842fb8e9511ca/javav2/example_code/iam/src/main/java/com/example/iam/AccessKeyLastUsed.java)。

## 啟用或停用存取金鑰
<a name="iam-access-keys-update"></a>

您可以透過建立 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/UpdateAccessKeyRequest.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/UpdateAccessKeyRequest.html) 物件、提供存取金鑰 ID、選擇性的使用者名稱和所需的 來啟用或停用存取金鑰[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/StatusType.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/StatusType.html)，然後將請求物件傳遞給 `IamClient’s``updateAccessKey`方法。

 **匯入** 

```
import software.amazon.awssdk.services.iam.model.IamException;
import software.amazon.awssdk.services.iam.model.StatusType;
import software.amazon.awssdk.services.iam.model.UpdateAccessKeyRequest;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.iam.IamClient;
```

 **Code** 

```
       public static void updateKey(IamClient iam, String username, String accessId, String status ) {

          try {
              if (status.toLowerCase().equalsIgnoreCase("active")) {
                  statusType = StatusType.ACTIVE;
              } else if (status.toLowerCase().equalsIgnoreCase("inactive")) {
                  statusType = StatusType.INACTIVE;
              } else {
                  statusType = StatusType.UNKNOWN_TO_SDK_VERSION;
              }
              UpdateAccessKeyRequest request = UpdateAccessKeyRequest.builder()
                .accessKeyId(accessId)
                .userName(username)
                .status(statusType)
                .build();

              iam.updateAccessKey(request);

              System.out.printf(
                "Successfully updated the status of access key %s to" +
                        "status %s for user %s", accessId, status, username);

        } catch (IamException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f807d60010caf3d14fe4cd0801b842fb8e9511ca/javav2/example_code/iam/src/main/java/com/example/iam/UpdateAccessKey.java)。

## 刪除存取金鑰
<a name="delete-an-access-key"></a>

若要永久刪除存取金鑰，請呼叫 `IamClient’s``deleteKey`方法，提供[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/DeleteAccessKeyRequest.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/DeleteAccessKeyRequest.html)包含存取金鑰 ID 和使用者名稱的 。

**注意**  
金鑰一旦刪除，就不能再擷取或使用。若要暫時停用金鑰以便稍後再次啟用，請改用 [`updateAccessKey`](#iam-access-keys-update)方法。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.iam.IamClient;
import software.amazon.awssdk.services.iam.model.DeleteAccessKeyRequest;
import software.amazon.awssdk.services.iam.model.IamException;
```

 **Code** 

```
    public static void deleteKey(IamClient iam ,String username, String accessKey ) {

        try {
            DeleteAccessKeyRequest request = DeleteAccessKeyRequest.builder()
                    .accessKeyId(accessKey)
                    .userName(username)
                    .build();

            iam.deleteAccessKey(request);
            System.out.println("Successfully deleted access key " + accessKey +
                " from user " + username);

        } catch (IamException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f807d60010caf3d14fe4cd0801b842fb8e9511ca/javav2/example_code/iam/src/main/java/com/example/iam/DeleteAccessKey.java)。

## 其他資訊
<a name="more-information"></a>
+  IAM API 參考中的 [CreateAccessKey](https://docs.aws.amazon.com/IAM/latest/APIReference/API_CreateAccessKey.html) 
+  IAM API 參考中的 [ListAccessKeys](https://docs.aws.amazon.com/IAM/latest/APIReference/API_ListAccessKeys.html) 
+  《 IAM API 參考》中的 [GetAccessKeyLastUsed](https://docs.aws.amazon.com/IAM/latest/APIReference/API_GetAccessKeyLastUsed.html) 
+  IAM API 參考中的 [UpdateAccessKey](https://docs.aws.amazon.com/IAM/latest/APIReference/API_UpdateAccessKey.html) 
+  IAM API 參考中的 [DeleteAccessKey](https://docs.aws.amazon.com/IAM/latest/APIReference/API_DeleteAccessKey.html) 

# 管理 IAM 使用者
<a name="examples-iam-users"></a>

## 建立使用者
<a name="creating-a-user"></a>

使用包含使用者名稱的 [CreateUserRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/CreateUserRequest.html) 物件，將使用者名稱 IAM 提供給 IamClient 的 `createUser`方法，以建立新的使用者。

 **匯入** 

```
import software.amazon.awssdk.core.waiters.WaiterResponse;
import software.amazon.awssdk.services.iam.model.CreateUserRequest;
import software.amazon.awssdk.services.iam.model.CreateUserResponse;
import software.amazon.awssdk.services.iam.model.IamException;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.iam.IamClient;
import software.amazon.awssdk.services.iam.waiters.IamWaiter;
import software.amazon.awssdk.services.iam.model.GetUserRequest;
import software.amazon.awssdk.services.iam.model.GetUserResponse;
```

 **Code** 

```
    public static String createIAMUser(IamClient iam, String username ) {

        try {
            // Create an IamWaiter object
            IamWaiter iamWaiter = iam.waiter();

            CreateUserRequest request = CreateUserRequest.builder()
                    .userName(username)
                    .build();

            CreateUserResponse response = iam.createUser(request);

            // Wait until the user is created
            GetUserRequest userRequest = GetUserRequest.builder()
                    .userName(response.user().userName())
                    .build();

            WaiterResponse<GetUserResponse> waitUntilUserExists = iamWaiter.waitUntilUserExists(userRequest);
            waitUntilUserExists.matched().response().ifPresent(System.out::println);
            return response.user().userName();

        } catch (IamException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
       return "";
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/cf25559da654a7b74bec039c0ab9397dc5951dd4/javav2/example_code/iam/src/main/java/com/example/iam/CreateUser.java)。

## 列出 使用者
<a name="listing-users"></a>

若要列出您帳戶 IAM 的使用者，請建立新的 [ListUsersRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/ListUsersRequest.html)，並將其傳遞給 IamClient 的 `listUsers`方法。您可以透過在傳回的 `users`ListUsersResponse[ 物件上呼叫 ](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/ListUsersResponse.html)，來擷取使用者清單。

`listUsers` 傳回的使用者清單會分頁。您可以呼叫回應物件的 `isTruncated` 方法，檢查是否有更多可擷取的結果。如果傳回 `true`，則請呼叫回應物件的 `marker()` 方法。使用標記值來建立新的請求物件。然後以新的請求再次呼叫 `listUsers` 方法。

 **匯入** 

```
import software.amazon.awssdk.services.iam.model.IamException;
import software.amazon.awssdk.services.iam.model.ListUsersRequest;
import software.amazon.awssdk.services.iam.model.ListUsersResponse;
import software.amazon.awssdk.services.iam.model.User;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.iam.IamClient;
```

 **Code** 

```
    public static void listAllUsers(IamClient iam ) {

        try {

             boolean done = false;
             String newMarker = null;

             while(!done) {
                ListUsersResponse response;

                if (newMarker == null) {
                    ListUsersRequest request = ListUsersRequest.builder().build();
                    response = iam.listUsers(request);
                } else {
                    ListUsersRequest request = ListUsersRequest.builder()
                        .marker(newMarker).build();
                    response = iam.listUsers(request);
                }

                for(User user : response.users()) {
                 System.out.format("\n Retrieved user %s", user.userName());
                }

                if(!response.isTruncated()) {
                  done = true;
                } else {
                    newMarker = response.marker();
                }
            }
        } catch (IamException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/cf25559da654a7b74bec039c0ab9397dc5951dd4/javav2/example_code/iam/src/main/java/com/example/iam/ListUsers.java)。

## 更新使用者
<a name="updating-a-user"></a>

若要更新使用者，請呼叫 IamClient 物件的 `updateUser`方法，該方法採用 [UpdateUserRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/UpdateUserRequest.html) 物件，您可以使用該物件來變更使用者名稱**或*路徑*。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.iam.IamClient;
import software.amazon.awssdk.services.iam.model.IamException;
import software.amazon.awssdk.services.iam.model.UpdateUserRequest;
```

 **Code** 

```
    public static void updateIAMUser(IamClient iam, String curName,String newName ) {

        try {
            UpdateUserRequest request = UpdateUserRequest.builder()
                    .userName(curName)
                    .newUserName(newName)
                    .build();

            iam.updateUser(request);
            System.out.printf("Successfully updated user to username %s",
                newName);
        } catch (IamException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
      }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/cf25559da654a7b74bec039c0ab9397dc5951dd4/javav2/example_code/iam/src/main/java/com/example/iam/UpdateUser.java)。

## 刪除使用者
<a name="deleting-a-user"></a>

若要刪除使用者，請使用 [UpdateUserRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/UpdateUserRequest.html) 物件集呼叫 IamClient 的`deleteUser`請求，並設定要刪除的使用者名稱。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.iam.IamClient;
import software.amazon.awssdk.services.iam.model.DeleteUserRequest;
import software.amazon.awssdk.services.iam.model.IamException;
```

 **Code** 

```
    public static void deleteIAMUser(IamClient iam, String userName) {

        try {
            DeleteUserRequest request = DeleteUserRequest.builder()
                    .userName(userName)
                    .build();

            iam.deleteUser(request);
            System.out.println("Successfully deleted IAM user " + userName);
        } catch (IamException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/cf25559da654a7b74bec039c0ab9397dc5951dd4/javav2/example_code/iam/src/main/java/com/example/iam/DeleteUser.java)。

## 詳細資訊
<a name="more-information"></a>
+  《 使用者指南[IAM](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users.html)》中的 IAM 使用者
+  IAM 《 使用者指南》中的[管理 IAM 使用者](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_manage.html) 
+  IAM API 參考中的 [CreateUser](https://docs.aws.amazon.com/IAM/latest/APIReference/API_CreateUser.html) 
+  IAM API 參考中的 [ListUsers](https://docs.aws.amazon.com/IAM/latest/APIReference/API_ListUsers.html) 
+  IAM API 參考中的 [UpdateUser](https://docs.aws.amazon.com/IAM/latest/APIReference/API_UpdateUser.html) 
+  IAM API 參考中的 [DeleteUser](https://docs.aws.amazon.com/IAM/latest/APIReference/API_DeleteUser.html) 

# 使用 建立 IAM 政策 AWS SDK for Java 2.x
<a name="feature-iam-policy-builder"></a>

[IAM 政策建置器 API](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/policybuilder/iam/package-summary.html) 是一個程式庫，可用於在 Java 中建置 [IAM 政策](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html)並將其上傳至 AWS Identity and Access Management (IAM)。

API 提供用戶端、物件導向方法來產生 JSON 字串，而不是透過手動組合 JSON 字串或讀取檔案來建置 IAM 政策。當您讀取 JSON 格式的現有 IAM 政策時，API 會將其轉換為 [IamPolicy](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/policybuilder/iam/IamPolicy.html) 執行個體進行處理。

IAM 政策建置器 API 隨 SDK 2.20.105 版推出，因此請在 Maven 建置檔案中使用該版本或更新版本。開發套件的最新版本編號[列於 Maven 中央](https://central.sonatype.com/artifact/software.amazon.awssdk/iam-policy-builder)。

下列程式碼片段顯示 Maven `pom.xml` 檔案的範例相依性區塊。這可讓您在專案中使用 IAM 政策建置器 API。

```
<dependency>
    <groupId>software.amazon.awssdk</groupId>
    <artifactId>iam-policy-builder</artifactId>
    <version>2.27.21</version>
</dependency>
```

## 建立 `IamPolicy`
<a name="iam-policy-builder-create"></a>

本節顯示數個如何使用 IAM 政策建置器 API 建置政策的範例。

在下列每個範例中，從 開始`[IamPolicy.Builder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/policybuilder/iam/IamPolicy.Builder.html)`，並使用 `addStatement`方法新增一或多個陳述式。在此模式之後，[IamStatement.Builder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/policybuilder/iam/IamStatement.Builder.html) 具有將效果、動作、資源和條件新增至陳述式的方法。

### 範例：建立以時間為基礎的政策
<a name="iam-policy-builder-create-ex-time-based"></a>

下列範例會建立身分型政策，允許在兩個時間點之間執行 Amazon DynamoDB `GetItem`動作。

```
    public String timeBasedPolicyExample() {
        IamPolicy policy = IamPolicy.builder()
                .addStatement(b -> b
                        .effect(IamEffect.ALLOW)
                        .addAction("dynamodb:GetItem")
                        .addResource(IamResource.ALL)
                        .addCondition(b1 -> b1
                                .operator(IamConditionOperator.DATE_GREATER_THAN)
                                .key("aws:CurrentTime")
                                .value("2020-04-01T00:00:00Z"))
                        .addCondition(b1 -> b1
                                .operator(IamConditionOperator.DATE_LESS_THAN)
                                .key("aws:CurrentTime")
                                .value("2020-06-30T23:59:59Z")))
                .build();

        // Use an IamPolicyWriter to write out the JSON string to a more readable format.
        return policy.toJson(IamPolicyWriter.builder()
                        .prettyPrint(true)
                        .build());
    }
```

#### JSON 輸出
<a name="iam-builder-ex-json-date"></a>

上一個範例中的最後一個陳述式會傳回下列 JSON 字串。

閱讀*AWS Identity and Access Management 《 使用者指南*》中有關此[範例](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_examples_aws-dates.html)的詳細資訊。

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": {
        "Effect": "Allow",
        "Action": "dynamodb:GetItem",
        "Resource": "*",
        "Condition": {
            "DateGreaterThan": {
                "aws:CurrentTime": "2020-04-01T00:00:00Z"
            },
            "DateLessThan": {
                "aws:CurrentTime": "2020-06-30T23:59:59Z"
            }
        }
    }
}
```

------

### 範例：指定多個條件
<a name="iam-policy-builder-create-ex-multi-conditions"></a>

下列範例示範如何建立身分型政策，允許存取特定 DynamoDB 屬性。政策包含兩個條件。

```
    public String multipleConditionsExample() {
        IamPolicy policy = IamPolicy.builder()
                .addStatement(b -> b
                        .effect(IamEffect.ALLOW)
                        .addAction("dynamodb:GetItem")
                        .addAction("dynamodb:BatchGetItem")
                        .addAction("dynamodb:Query")
                        .addAction("dynamodb:PutItem")
                        .addAction("dynamodb:UpdateItem")
                        .addAction("dynamodb:DeleteItem")
                        .addAction("dynamodb:BatchWriteItem")
                        .addResource("arn:aws:dynamodb:*:*:table/table-name")
                        .addConditions(IamConditionOperator.STRING_EQUALS.addPrefix("ForAllValues:"),
                                "dynamodb:Attributes",
                                List.of("column-name1", "column-name2", "column-name3"))
                        .addCondition(b1 -> b1.operator(IamConditionOperator.STRING_EQUALS.addSuffix("IfExists"))
                                .key("dynamodb:Select")
                                .value("SPECIFIC_ATTRIBUTES")))
                .build();

        return policy.toJson(IamPolicyWriter.builder()
                .prettyPrint(true).build());
    }
```

#### JSON 輸出
<a name="iam-builder-ex-json-multi-cond"></a>

上一個範例中的最後一個陳述式會傳回下列 JSON 字串。

閱讀*AWS Identity and Access Management 《 使用者指南*》中有關此[範例](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_examples_dynamodb_attributes.html)的詳細資訊。

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": {
        "Effect": "Allow",
        "Action": [
            "dynamodb:GetItem",
            "dynamodb:BatchGetItem",
            "dynamodb:Query",
            "dynamodb:PutItem",
            "dynamodb:UpdateItem",
            "dynamodb:DeleteItem",
            "dynamodb:BatchWriteItem"
        ],
        "Resource": "arn:aws:dynamodb:*:*:table/table-name",
        "Condition": {
            "ForAllValues:StringEquals": {
                "dynamodb:Attributes": [
                    "column-name1",
                    "column-name2",
                    "column-name3"
                ]
            },
            "StringEqualsIfExists": {
                "dynamodb:Select": "SPECIFIC_ATTRIBUTES"
            }
        }
    }
}
```

------

### 範例：指定主體
<a name="iam-policy-builder-create-ex-principals"></a>

下列範例示範如何建立以資源為基礎的政策，拒絕存取條件中指定的所有主體的儲存貯體。

```
    public String specifyPrincipalsExample() {
        IamPolicy policy = IamPolicy.builder()
                .addStatement(b -> b
                        .effect(IamEffect.DENY)
                        .addAction("s3:*")
                        .addPrincipal(IamPrincipal.ALL)
                        .addResource("arn:aws:s3:::BUCKETNAME/*")
                        .addResource("arn:aws:s3:::BUCKETNAME")
                        .addCondition(b1 -> b1
                                .operator(IamConditionOperator.ARN_NOT_EQUALS)
                                .key("aws:PrincipalArn")
                                .value("arn:aws:iam::444455556666:user/user-name")))
                .build();
        return policy.toJson(IamPolicyWriter.builder()
                .prettyPrint(true).build());
    }
```

#### JSON 輸出
<a name="iam-policy-builder-create-json-ex-principals"></a>

上一個範例中的最後一個陳述式會傳回下列 JSON 字串。

閱讀*AWS Identity and Access Management 《 使用者指南*》中有關此[範例](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_principal.html#principal-anonymous)的詳細資訊。

------
#### [ JSON ]

****  

```
{
  "Version":"2012-10-17",		 	 	 
  "Statement" : {
    "Effect" : "Deny",
    "Principal" : "*",
    "Action" : "s3:*",
    "Resource" : [ "arn:aws:s3:::BUCKETNAME/*", "arn:aws:s3:::BUCKETNAME" ],
    "Condition" : {
      "ArnNotEquals" : {
        "aws:PrincipalArn" : "arn:aws:iam::444455556666:user/user-name"
      }
    }
  }
}
```

------

### 範例：允許跨帳戶存取
<a name="iam-policy-builder-create-ex-cross-account"></a>

下列範例顯示如何允許另一個 AWS 帳戶 將物件上傳至您的儲存貯體，同時保留已上傳物件的完整擁有者控制。

```
    public String allowCrossAccountAccessExample() {
        IamPolicy policy = IamPolicy.builder()
                .addStatement(b -> b
                        .effect(IamEffect.ALLOW)
                        .addPrincipal(IamPrincipalType.AWS, "111122223333")
                        .addAction("s3:PutObject")
                        .addResource("arn:aws:s3:::amzn-s3-demo-bucket/*")
                        .addCondition(b1 -> b1
                                .operator(IamConditionOperator.STRING_EQUALS)
                                .key("s3:x-amz-acl")
                                .value("bucket-owner-full-control")))
                .build();
        return policy.toJson(IamPolicyWriter.builder()
                .prettyPrint(true).build());
    }
```

#### JSON 輸出
<a name="iam-policy-builder-create-ex-json-cross-account"></a>

上一個範例中的最後一個陳述式會傳回下列 JSON 字串。

閱讀《*Amazon Simple Storage Service 使用者指南*》中有關此[範例](https://docs.aws.amazon.com/AmazonS3/latest/userguide/example-bucket-policies.html#example-bucket-policies-acl-2)的詳細資訊。

------
#### [ JSON ]

****  

```
{
  "Version":"2012-10-17",		 	 	 
  "Statement" : {
    "Effect" : "Allow",
    "Principal" : {
      "AWS" : "111122223333"
    },
    "Action" : "s3:PutObject",
    "Resource" : "arn:aws:s3:::amzn-s3-demo-bucket/*",
    "Condition" : {
      "StringEquals" : {
        "s3:x-amz-acl" : "bucket-owner-full-control"
      }
    }
  }
}
```

------

## `IamPolicy` 搭配 IAM 使用
<a name="iam-policy-builder-work-with-service"></a>

建立`IamPolicy`執行個體之後，您可以使用 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/IamClient.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/IamClient.html) 來使用 IAM 服務。

下列範例會建置政策，允許 [IAM 身分](https://docs.aws.amazon.com/IAM/latest/UserGuide/id.html)將項目寫入使用 `accountID` 參數指定的帳戶中的 DynamoDB 資料表。然後，政策會以 JSON 字串的形式上傳至 IAM。

```
    public String createAndUploadPolicyExample(IamClient iam, String accountID, String policyName) {
        // Build the policy.
        IamPolicy policy =
                IamPolicy.builder() // 'version' defaults to "2012-10-17".
                        .addStatement(IamStatement.builder()
                                .effect(IamEffect.ALLOW)
                                .addAction("dynamodb:PutItem")
                                .addResource("arn:aws:dynamodb:us-east-1:" + accountID + ":table/exampleTableName")
                                .build())
                        .build();
        // Upload the policy.
        iam.createPolicy(r -> r.policyName(policyName).policyDocument(policy.toJson()));
        return policy.toJson(IamPolicyWriter.builder().prettyPrint(true).build());
    }
```

下一個範例以先前的範例為基礎。程式碼會下載政策，並透過複製和修改陳述式，將其用作新政策的基礎。然後上傳新的政策。

```
    public String createNewBasedOnExistingPolicyExample(IamClient iam, String accountID, String policyName, String newPolicyName) {

        String policyArn = "arn:aws:iam::" + accountID + ":policy/" + policyName;
        GetPolicyResponse getPolicyResponse = iam.getPolicy(r -> r.policyArn(policyArn));

        String policyVersion = getPolicyResponse.policy().defaultVersionId();
        GetPolicyVersionResponse getPolicyVersionResponse =
                iam.getPolicyVersion(r -> r.policyArn(policyArn).versionId(policyVersion));

        // Create an IamPolicy instance from the JSON string returned from IAM.
        String decodedPolicy = URLDecoder.decode(getPolicyVersionResponse.policyVersion().document(), StandardCharsets.UTF_8);
        IamPolicy policy = IamPolicy.fromJson(decodedPolicy);

            /*
             All IamPolicy components are immutable, so use the copy method that creates a new instance that
             can be altered in the same method call.

             Add the ability to get an item from DynamoDB as an additional action.
            */
        IamStatement newStatement = policy.statements().get(0).copy(s -> s.addAction("dynamodb:GetItem"));

        // Create a new statement that replaces the original statement.
        IamPolicy newPolicy = policy.copy(p -> p.statements(Arrays.asList(newStatement)));

        // Upload the new policy. IAM now has both policies.
        iam.createPolicy(r -> r.policyName(newPolicyName)
                .policyDocument(newPolicy.toJson()));

        return newPolicy.toJson(IamPolicyWriter.builder().prettyPrint(true).build());
    }
```

### IamClient
<a name="iam-policy-builder-work-with-serivce-create-client"></a>

上述範例使用建立的 `IamClient` 引數，如下列程式碼片段所示。

```
IamClient iam = IamClient.builder().region(Region.AWS_GLOBAL).build();
```

### JSON 中的政策
<a name="iam-policy-builder-work-with-serivce-json"></a>

這些範例會傳回下列 JSON 字串。

```
First example
{
  "Version": "2012-10-17",		 	 	 
  "Statement" : {
    "Effect" : "Allow",
    "Action" : "dynamodb:PutItem",
    "Resource" : "arn:aws:dynamodb:us-east-1:111122223333:table/exampleTableName"
  }
}

Second example
{
  "Version": "2012-10-17",		 	 	 
  "Statement" : {
    "Effect" : "Allow",
    "Action" : [ "dynamodb:PutItem", "dynamodb:GetItem" ],
    "Resource" : "arn:aws:dynamodb:us-east-1:111122223333:table/exampleTableName"
  }
}
```

# 使用 IAM 政策
<a name="examples-iam-policies"></a>

## 建立政策
<a name="create-a-policy"></a>

若要建立新的政策，請在 [CreatePolicyRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/CreatePolicyRequest.html) 中提供政策的名稱和 JSON 格式的政策文件給 IamClient 的 `createPolicy`方法。

 **匯入** 

```
import software.amazon.awssdk.core.waiters.WaiterResponse;
import software.amazon.awssdk.services.iam.model.CreatePolicyRequest;
import software.amazon.awssdk.services.iam.model.CreatePolicyResponse;
import software.amazon.awssdk.services.iam.model.GetPolicyRequest;
import software.amazon.awssdk.services.iam.model.GetPolicyResponse;
import software.amazon.awssdk.services.iam.model.IamException;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.iam.IamClient;
import software.amazon.awssdk.services.iam.waiters.IamWaiter;
```

 **Code** 

```
    public static String createIAMPolicy(IamClient iam, String policyName ) {

        try {
            // Create an IamWaiter object
            IamWaiter iamWaiter = iam.waiter();

            CreatePolicyRequest request = CreatePolicyRequest.builder()
                .policyName(policyName)
                .policyDocument(PolicyDocument).build();

            CreatePolicyResponse response = iam.createPolicy(request);

            // Wait until the policy is created
            GetPolicyRequest polRequest = GetPolicyRequest.builder()
                    .policyArn(response.policy().arn())
                    .build();

            WaiterResponse<GetPolicyResponse> waitUntilPolicyExists = iamWaiter.waitUntilPolicyExists(polRequest);
            waitUntilPolicyExists.matched().response().ifPresent(System.out::println);
            return response.policy().arn();

         } catch (IamException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
        return "" ;
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/e41bacfd5e671e7ef1a9f73e972540dc6a434664/javav2/example_code/iam/src/main/java/com/example/iam/CreatePolicy.java)。

## 取得政策
<a name="get-a-policy"></a>

若要擷取現有政策，請呼叫 IamClient 的 `getPolicy`方法，在 [GetPolicyRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/GetPolicyRequest.html) 物件中提供政策的 ARN。

 **匯入** 

```
import software.amazon.awssdk.services.iam.model.GetPolicyRequest;
import software.amazon.awssdk.services.iam.model.GetPolicyResponse;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.iam.IamClient;
import software.amazon.awssdk.services.iam.model.IamException;
```

 **Code** 

```
    public static void getIAMPolicy(IamClient iam, String policyArn) {

        try {
            GetPolicyRequest request = GetPolicyRequest.builder()
                .policyArn(policyArn).build();

            GetPolicyResponse response = iam.getPolicy(request);
            System.out.format("Successfully retrieved policy %s",
                response.policy().policyName());

        } catch (IamException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/cf25559da654a7b74bec039c0ab9397dc5951dd4/javav2/example_code/iam/src/main/java/com/example/iam/GetPolicy.java)。

## 連接角色政策
<a name="attach-a-role-policy"></a>

您可以透過呼叫 IamClient 的 `attachRolePolicy`方法，在 [AttachRolePolicyRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/AttachRolePolicyRequest.html) 中為其提供角色名稱和政策 ARN，[https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles.html](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles.html)將政策 IAM 連接到角色。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.iam.IamClient;
import software.amazon.awssdk.services.iam.model.IamException;
import software.amazon.awssdk.services.iam.model.AttachRolePolicyRequest;
import software.amazon.awssdk.services.iam.model.AttachedPolicy;
import software.amazon.awssdk.services.iam.model.ListAttachedRolePoliciesRequest;
import software.amazon.awssdk.services.iam.model.ListAttachedRolePoliciesResponse;
import java.util.List;
```

 **Code** 

```
    public static void attachIAMRolePolicy(IamClient iam, String roleName, String policyArn ) {

        try {

             ListAttachedRolePoliciesRequest request = ListAttachedRolePoliciesRequest.builder()
                    .roleName(roleName)
                    .build();

            ListAttachedRolePoliciesResponse  response = iam.listAttachedRolePolicies(request);
            List<AttachedPolicy> attachedPolicies = response.attachedPolicies();

            // Ensure that the policy is not attached to this role
            String polArn = "";
            for (AttachedPolicy policy: attachedPolicies) {
                polArn = policy.policyArn();
                if (polArn.compareTo(policyArn)==0) {
                   System.out.println(roleName +
                            " policy is already attached to this role.");
                    return;
                }
          }

            AttachRolePolicyRequest attachRequest =
                AttachRolePolicyRequest.builder()
                        .roleName(roleName)
                        .policyArn(policyArn)
                        .build();

            iam.attachRolePolicy(attachRequest);

            System.out.println("Successfully attached policy " + policyArn +
                " to role " + roleName);

         } catch (IamException e) {
                System.err.println(e.awsErrorDetails().errorMessage());
                System.exit(1);
          }

     System.out.println("Done");
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/cf25559da654a7b74bec039c0ab9397dc5951dd4/javav2/example_code/iam/src/main/java/com/example/iam/AttachRolePolicy.java)。

## 列出連接的角色政策
<a name="list-attached-role-policies"></a>

呼叫 IamClient 的 `listAttachedRolePolicies`方法，列出角色上的附加政策。它採用 [ListAttachedRolePoliciesRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/ListAttachedRolePoliciesRequest.html) 物件，其中包含要列出其政策的角色名稱。

在傳回的 [ListAttachedRolePoliciesResponse](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/ListAttachedRolePoliciesResponse.html) 物件`getAttachedPolicies`上呼叫 ，以取得連接的政策清單。結果可能會被截斷；如果 `ListAttachedRolePoliciesResponse` 物件的 `isTruncated` 方法傳回 `true`，請呼叫 `ListAttachedRolePoliciesResponse` 物件的 `marker` 方法。使用傳回的標記來建立新的請求，並使用它再次呼叫 `listAttachedRolePolicies` 以取得下一個結果批次。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.iam.IamClient;
import software.amazon.awssdk.services.iam.model.IamException;
import software.amazon.awssdk.services.iam.model.AttachRolePolicyRequest;
import software.amazon.awssdk.services.iam.model.AttachedPolicy;
import software.amazon.awssdk.services.iam.model.ListAttachedRolePoliciesRequest;
import software.amazon.awssdk.services.iam.model.ListAttachedRolePoliciesResponse;
import java.util.List;
```

 **Code** 

```
    public static void attachIAMRolePolicy(IamClient iam, String roleName, String policyArn ) {

        try {

             ListAttachedRolePoliciesRequest request = ListAttachedRolePoliciesRequest.builder()
                    .roleName(roleName)
                    .build();

            ListAttachedRolePoliciesResponse  response = iam.listAttachedRolePolicies(request);
            List<AttachedPolicy> attachedPolicies = response.attachedPolicies();

            // Ensure that the policy is not attached to this role
            String polArn = "";
            for (AttachedPolicy policy: attachedPolicies) {
                polArn = policy.policyArn();
                if (polArn.compareTo(policyArn)==0) {
                   System.out.println(roleName +
                            " policy is already attached to this role.");
                    return;
                }
          }

            AttachRolePolicyRequest attachRequest =
                AttachRolePolicyRequest.builder()
                        .roleName(roleName)
                        .policyArn(policyArn)
                        .build();

            iam.attachRolePolicy(attachRequest);

            System.out.println("Successfully attached policy " + policyArn +
                " to role " + roleName);

         } catch (IamException e) {
                System.err.println(e.awsErrorDetails().errorMessage());
                System.exit(1);
          }

     System.out.println("Done");
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/cf25559da654a7b74bec039c0ab9397dc5951dd4/javav2/example_code/iam/src/main/java/com/example/iam/AttachRolePolicy.java)。

## 分開角色政策
<a name="detach-a-role-policy"></a>

若要從角色分離政策，請呼叫 IamClient 的 `detachRolePolicy`方法，在 [DetachRolePolicyRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/DetachRolePolicyRequest.html) 中提供角色名稱和政策 ARN。

 **匯入** 

```
import software.amazon.awssdk.services.iam.model.DetachRolePolicyRequest;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.iam.IamClient;
import software.amazon.awssdk.services.iam.model.IamException;
```

 **Code** 

```
    public static void detachPolicy(IamClient iam, String roleName, String policyArn ) {

        try {
            DetachRolePolicyRequest request = DetachRolePolicyRequest.builder()
                    .roleName(roleName)
                    .policyArn(policyArn)
                    .build();

            iam.detachRolePolicy(request);
            System.out.println("Successfully detached policy " + policyArn +
                " from role " + roleName);

        } catch (IamException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/cf25559da654a7b74bec039c0ab9397dc5951dd4/javav2/example_code/iam/src/main/java/com/example/iam/DetachRolePolicy.java)。

## 其他資訊
<a name="more-information"></a>
+  IAM 《 使用者指南》中的[IAM 政策概觀](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html)。
+  IAM 《 使用者指南》中的 [AWS IAM 政策參考](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies.html)。
+  IAM API 參考中的 [CreatePolicy](https://docs.aws.amazon.com/IAM/latest/APIReference/API_CreatePolicy.html) 
+  IAM API 參考中的 [GetPolicy](https://docs.aws.amazon.com/IAM/latest/APIReference/API_GetPolicy.html) 
+  IAM API 參考中的 [AttachRolePolicy](https://docs.aws.amazon.com/IAM/latest/APIReference/API_AttachRolePolicy.html) 
+  IAM API 參考中的 [ListAttachedRolePolicies](https://docs.aws.amazon.com/IAM/latest/APIReference/API_ListAttachedRolePolicies.html) 
+  《 IAM API 參考》中的 [DetachRolePolicy](https://docs.aws.amazon.com/IAM/latest/APIReference/API_DetachRolePolicy.html) 

# 使用 IAM 伺服器憑證
<a name="examples-iam-server-certificates"></a>

若要啟用網站或應用程式的 HTTPS 連線 AWS，您需要 SSL/TLS *伺服器憑證*。您可以使用 提供的伺服器憑證， AWS Certificate Manager 或是從外部供應商取得的憑證。

建議您使用 ACM 來佈建、管理和部署伺服器憑證。透過 ACM ，您可以請求憑證、將其部署到您的 AWS 資源，並讓 為您 ACM 處理憑證續約。提供的憑證 ACM 是免費的。如需 的詳細資訊 ACM，請參閱[AWS Certificate Manager 《 使用者指南》](https://docs.aws.amazon.com/acm/latest/userguide/acm-overview.html)。

## 取得伺服器憑證
<a name="get-a-server-certificate"></a>

您可以透過呼叫 IamClient 的 `getServerCertificate`方法來擷取伺服器憑證，並使用憑證的名稱傳遞 [GetServerCertificateRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/GetServerCertificateRequest.html)。

 **匯入** 

```
import software.amazon.awssdk.services.iam.model.GetServerCertificateRequest;
import software.amazon.awssdk.services.iam.model.GetServerCertificateResponse;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.iam.IamClient;
import software.amazon.awssdk.services.iam.model.IamException;
```

 **Code** 

```
    public static void getCertificate(IamClient iam,String certName ) {

        try {
            GetServerCertificateRequest request = GetServerCertificateRequest.builder()
                    .serverCertificateName(certName)
                    .build();

            GetServerCertificateResponse response = iam.getServerCertificate(request);
            System.out.format("Successfully retrieved certificate with body %s",
                response.serverCertificate().certificateBody());

         } catch (IamException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/iam/src/main/java/com/example/iam/GetServerCertificate.java)。

## 列出伺服器憑證
<a name="list-server-certificates"></a>

若要列出您的伺服器憑證，請使用 [ListServerCertificatesRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/ListServerCertificatesRequest.html) 呼叫 IamClient 的 `listServerCertificates`方法。它會傳回 [ListServerCertificatesResponse](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/ListServerCertificatesResponse.html)。

呼叫傳回 `ListServerCertificateResponse` 物件的 `serverCertificateMetadataList` 方法以取得 [ServerCertificateMetadata](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/ServerCertificateMetadata.html) 物件的清單，您可以用來取得每個憑證的相關資訊。

結果可能遭到截斷；如果 `ListServerCertificateResponse` 物件的 `isTruncated` 方法傳回 `true`，請呼叫 `ListServerCertificatesResponse` 物件的 `marker` 方法並使用標記來建立新的請求。使用新的請求再次呼叫 `listServerCertificates`，以取得下一個結果批次。

 **匯入** 

```
import software.amazon.awssdk.services.iam.model.IamException;
import software.amazon.awssdk.services.iam.model.ListServerCertificatesRequest;
import software.amazon.awssdk.services.iam.model.ListServerCertificatesResponse;
import software.amazon.awssdk.services.iam.model.ServerCertificateMetadata;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.iam.IamClient;
```

 **Code** 

```
    public static void listCertificates(IamClient iam) {

        try {
            boolean done = false;
            String newMarker = null;

            while(!done) {
              ListServerCertificatesResponse response;

            if (newMarker == null) {
                ListServerCertificatesRequest request =
                        ListServerCertificatesRequest.builder().build();
                response = iam.listServerCertificates(request);
            } else {
                ListServerCertificatesRequest request =
                        ListServerCertificatesRequest.builder()
                                .marker(newMarker).build();
                response = iam.listServerCertificates(request);
            }

            for(ServerCertificateMetadata metadata :
                    response.serverCertificateMetadataList()) {
                System.out.printf("Retrieved server certificate %s",
                        metadata.serverCertificateName());
            }

            if(!response.isTruncated()) {
                done = true;
            } else {
                newMarker = response.marker();
            }
        }

        } catch (IamException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/iam/src/main/java/com/example/iam/ListServerCertificates.java)。

## 更新伺服器憑證
<a name="update-a-server-certificate"></a>

您可以透過呼叫 IamClient 的 `updateServerCertificate`方法來更新伺服器憑證的名稱或路徑。它需要設定 [UpdateServerCertificateRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/UpdateServerCertificateRequest.html) 物件，並搭配伺服器憑證的目前名稱以及要使用的新名稱或新路徑。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.iam.IamClient;
import software.amazon.awssdk.services.iam.model.IamException;
import software.amazon.awssdk.services.iam.model.UpdateServerCertificateRequest;
import software.amazon.awssdk.services.iam.model.UpdateServerCertificateResponse;
```

 **Code** 

```
    public static void updateCertificate(IamClient iam, String curName, String newName) {

        try {
            UpdateServerCertificateRequest request =
                UpdateServerCertificateRequest.builder()
                        .serverCertificateName(curName)
                        .newServerCertificateName(newName)
                        .build();

            UpdateServerCertificateResponse response =
                iam.updateServerCertificate(request);


            System.out.printf("Successfully updated server certificate to name %s",
                newName);

        } catch (IamException e) {
             System.err.println(e.awsErrorDetails().errorMessage());
             System.exit(1);
        }
     }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/iam/src/main/java/com/example/iam/UpdateServerCertificate.java)。

## 刪除伺服器憑證
<a name="delete-a-server-certificate"></a>

若要刪除伺服器憑證，請使用包含憑證名稱的 [DeleteServerCertificateRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/DeleteServerCertificateRequest.html) 呼叫 IamClient 的 `deleteServerCertificate`方法。

 **匯入** 

```
import software.amazon.awssdk.services.iam.model.DeleteServerCertificateRequest;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.iam.IamClient;
import software.amazon.awssdk.services.iam.model.IamException;
```

 **Code** 

```
    public static void deleteCert(IamClient iam,String certName ) {

        try {
            DeleteServerCertificateRequest request =
                DeleteServerCertificateRequest.builder()
                        .serverCertificateName(certName)
                        .build();

            iam.deleteServerCertificate(request);
            System.out.println("Successfully deleted server certificate " +
                    certName);

        } catch (IamException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/iam/src/main/java/com/example/iam/DeleteServerCertificate.java)。

## 其他資訊
<a name="more-information"></a>
+  《 IAM 使用者指南[》中的使用伺服器憑證](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_server-certs.html) 
+  IAM API 參考中的 [GetServerCertificate](https://docs.aws.amazon.com/IAM/latest/APIReference/API_GetServerCertificate.html) 
+  IAM API 參考中的 [ListServerCertificates](https://docs.aws.amazon.com/IAM/latest/APIReference/API_ListServerCertificates.html) 
+  IAM API 參考中的 [UpdateServerCertificate](https://docs.aws.amazon.com/IAM/latest/APIReference/API_UpdateServerCertificate.html) 
+  IAM API 參考中的 [DeleteServerCertificate](https://docs.aws.amazon.com/IAM/latest/APIReference/API_DeleteServerCertificate.html) 
+  [AWS Certificate Manager 使用者指南](https://docs.aws.amazon.com/acm/latest/userguide/) 

# 使用 Kinesis
<a name="examples-kinesis"></a>

本節提供[Amazon Kinesis](https://docs.aws.amazon.com/kinesis/)使用 適用於 Java 的 AWS SDK 2.x 進行程式設計的範例。

如需 的詳細資訊 Kinesis，請參閱 [Amazon Kinesis 開發人員指南](https://docs.aws.amazon.com/streams/latest/dev/introduction.html)。

下列範例僅包含示範每個技術所需的程式碼。[GitHub 上提供完整程式碼範例](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2)。您可以從那裡下載單一原始檔案或將儲存庫複製到本機，以取得建置和執行的所有範例。

**Topics**
+ [訂閱 Amazon Kinesis Data Streams](examples-kinesis-stream.md)

# 訂閱 Amazon Kinesis Data Streams
<a name="examples-kinesis-stream"></a>

下列範例說明如何使用 `subscribeToShard`方法從 Amazon Kinesis 資料串流擷取和處理資料。 Kinesis Data Streams 現採用增強的廣發功能和低延遲 HTTP/2 資料擷取 API，讓開發人員更輕鬆地在相同的 Kinesis 資料串流上執行多個低延遲、高效能的應用程式。

## 設定
<a name="set-up"></a>

首先，建立非同步 Kinesis 用戶端和 [SubscribeToShardRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/kinesis/model/SubscribeToShardRequest.html) 物件。這些物件用於下列每個範例，以訂閱 Kinesis 事件。

 **匯入** 

```
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import software.amazon.awssdk.core.async.SdkPublisher;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.kinesis.KinesisAsyncClient;
import software.amazon.awssdk.services.kinesis.model.ShardIteratorType;
import software.amazon.awssdk.services.kinesis.model.SubscribeToShardEvent;
import software.amazon.awssdk.services.kinesis.model.SubscribeToShardEventStream;
import software.amazon.awssdk.services.kinesis.model.SubscribeToShardRequest;
import software.amazon.awssdk.services.kinesis.model.SubscribeToShardResponse;
import software.amazon.awssdk.services.kinesis.model.SubscribeToShardResponseHandler;
```

 **Code** 

```
        Region region = Region.US_EAST_1;
        KinesisAsyncClient client = KinesisAsyncClient.builder()
        .region(region)
        .build();

        SubscribeToShardRequest request = SubscribeToShardRequest.builder()
                .consumerARN(CONSUMER_ARN)
                .shardId("arn:aws:kinesis:us-east-1:111122223333:stream/StockTradeStream")
                .startingPosition(s -> s.type(ShardIteratorType.LATEST)).build();
```

## 使用建置器界面
<a name="use-the-builder-interface"></a>

您可以使用 `builder` 方法，以簡化建立 [SubscribeToShardResponseHandler](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/kinesis/model/SubscribeToShardResponseHandler.html)。

使用 builder，您可以使用方法呼叫來設定每個生命週期回呼，而不用實作完整的介面。

 **Code** 

```
    private static CompletableFuture<Void> responseHandlerBuilder(KinesisAsyncClient client, SubscribeToShardRequest request) {
        SubscribeToShardResponseHandler responseHandler = SubscribeToShardResponseHandler
                .builder()
                .onError(t -> System.err.println("Error during stream - " + t.getMessage()))
                .onComplete(() -> System.out.println("All records stream successfully"))
                // Must supply some type of subscriber
                .subscriber(e -> System.out.println("Received event - " + e))
                .build();
        return client.subscribeToShard(request, responseHandler);
    }
```

如需進一步控制發佈者，您可以使用 `publisherTransformer` 方法來自訂發佈者。

 **Code** 

```
    private static CompletableFuture<Void> responseHandlerBuilderPublisherTransformer(KinesisAsyncClient client, SubscribeToShardRequest request) {
        SubscribeToShardResponseHandler responseHandler = SubscribeToShardResponseHandler
                .builder()
                .onError(t -> System.err.println("Error during stream - " + t.getMessage()))
                .publisherTransformer(p -> p.filter(e -> e instanceof SubscribeToShardEvent).limit(100))
                .subscriber(e -> System.out.println("Received event - " + e))
                .build();
        return client.subscribeToShard(request, responseHandler);
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/ac748d8ef99cd17e297cb74fe13aa671e2679088/javav2/example_code/kinesis/src/main/java/com/example/kinesis/KinesisStreamEx.java)。

## 使用自訂回應處理常式
<a name="use-a-custom-response-handler"></a>

若要完全控制訂閱者和發佈者，請實作 `SubscribeToShardResponseHandler` 界面。

在這個範例中，您將實作 `onEventStream` 方法，允許您完整存取發佈者。此範例示範如何將發佈者轉換為事件記錄，供訂閱者列印。

 **Code** 

```
    private static CompletableFuture<Void> responseHandlerBuilderClassic(KinesisAsyncClient client, SubscribeToShardRequest request) {
        SubscribeToShardResponseHandler responseHandler = new SubscribeToShardResponseHandler() {

            @Override
            public void responseReceived(SubscribeToShardResponse response) {
                System.out.println("Receieved initial response");
            }

            @Override
            public void onEventStream(SdkPublisher<SubscribeToShardEventStream> publisher) {
                publisher
                        // Filter to only SubscribeToShardEvents
                        .filter(SubscribeToShardEvent.class)
                        // Flat map into a publisher of just records
                        .flatMapIterable(SubscribeToShardEvent::records)
                        // Limit to 1000 total records
                        .limit(1000)
                        // Batch records into lists of 25
                        .buffer(25)
                        // Print out each record batch
                        .subscribe(batch -> System.out.println("Record Batch - " + batch));
            }

            @Override
            public void complete() {
                System.out.println("All records stream successfully");
            }

            @Override
            public void exceptionOccurred(Throwable throwable) {
                System.err.println("Error during stream - " + throwable.getMessage());
            }
        };
        return client.subscribeToShard(request, responseHandler);
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/ac748d8ef99cd17e297cb74fe13aa671e2679088/javav2/example_code/kinesis/src/main/java/com/example/kinesis/KinesisStreamEx.java)。

## 使用訪客界面
<a name="use-the-visitor-interface"></a>

您可以使用 [Visitor](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/kinesis/model/SubscribeToShardResponseHandler.Visitor.html) 物件，來訂閱您有興趣觀看的特定事件。

 **Code** 

```
    private static CompletableFuture<Void> responseHandlerBuilderVisitorBuilder(KinesisAsyncClient client, SubscribeToShardRequest request) {
        SubscribeToShardResponseHandler.Visitor visitor = SubscribeToShardResponseHandler.Visitor
                .builder()
                .onSubscribeToShardEvent(e -> System.out.println("Received subscribe to shard event " + e))
                .build();
        SubscribeToShardResponseHandler responseHandler = SubscribeToShardResponseHandler
                .builder()
                .onError(t -> System.err.println("Error during stream - " + t.getMessage()))
                .subscriber(visitor)
                .build();
        return client.subscribeToShard(request, responseHandler);
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/ac748d8ef99cd17e297cb74fe13aa671e2679088/javav2/example_code/kinesis/src/main/java/com/example/kinesis/KinesisStreamEx.java)。

## 使用自訂訂閱者
<a name="use-a-custom-subscriber"></a>

您也可以實作您自己的自訂訂閱者，來訂閱串流。

此程式碼片段說明範例訂閱者。

 **Code** 

```
    private static class MySubscriber implements Subscriber<SubscribeToShardEventStream> {

        private Subscription subscription;
        private AtomicInteger eventCount = new AtomicInteger(0);

        @Override
        public void onSubscribe(Subscription subscription) {
            this.subscription = subscription;
            this.subscription.request(1);
        }

        @Override
        public void onNext(SubscribeToShardEventStream shardSubscriptionEventStream) {
            System.out.println("Received event " + shardSubscriptionEventStream);
            if (eventCount.incrementAndGet() >= 100) {
                // You can cancel the subscription at any time if you wish to stop receiving events.
                subscription.cancel();
            }
            subscription.request(1);
        }

        @Override
        public void onError(Throwable throwable) {
            System.err.println("Error occurred while stream - " + throwable.getMessage());
        }

        @Override
        public void onComplete() {
            System.out.println("Finished streaming all events");
        }
    }
```

您可以將自訂訂閱者傳遞至 `subscribe`方法，如下列程式碼片段所示。

 **Code** 

```
    private static CompletableFuture<Void> responseHandlerBuilderSubscriber(KinesisAsyncClient client, SubscribeToShardRequest request) {
        SubscribeToShardResponseHandler responseHandler = SubscribeToShardResponseHandler
                .builder()
                .onError(t -> System.err.println("Error during stream - " + t.getMessage()))
                .subscriber(MySubscriber::new)
                .build();
        return client.subscribeToShard(request, responseHandler);
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/ac748d8ef99cd17e297cb74fe13aa671e2679088/javav2/example_code/kinesis/src/main/java/com/example/kinesis/KinesisStreamEx.java)。

## 將資料記錄寫入 Kinesis 資料串流
<a name="write-data-records-into-a-kinesis-data-stream"></a>

您可以使用 [KinesisClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/kinesis/KinesisClient.html) 物件，透過 `putRecords`方法將資料記錄寫入 Kinesis 資料串流。若要成功呼叫此方法，請建立 [PutRecordsRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/kinesis/model/PutRecordsRequest.html) 物件。您可以將資料串流的名稱傳遞給 `streamName`方法。而且必須使用 `putRecords` 方法傳遞資料 (如下列程式碼範例所示)。

 **匯入** 

```
import software.amazon.awssdk.core.SdkBytes;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.kinesis.KinesisClient;
import software.amazon.awssdk.services.kinesis.model.PutRecordRequest;
import software.amazon.awssdk.services.kinesis.model.KinesisException;
import software.amazon.awssdk.services.kinesis.model.DescribeStreamRequest;
import software.amazon.awssdk.services.kinesis.model.DescribeStreamResponse;
```

在下列 Java 程式碼範例中，請注意 **StockTrade** 物件會用作寫入 Kinesis 資料串流的資料。在執行此範例之前，請確定您已建立資料串流。

 **Code** 

```
import software.amazon.awssdk.core.SdkBytes;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.kinesis.KinesisClient;
import software.amazon.awssdk.services.kinesis.model.PutRecordRequest;
import software.amazon.awssdk.services.kinesis.model.KinesisException;
import software.amazon.awssdk.services.kinesis.model.DescribeStreamRequest;
import software.amazon.awssdk.services.kinesis.model.DescribeStreamResponse;

/**
 * Before running this Java V2 code example, set up your development
 * environment, including your credentials.
 *
 * For more information, see the following documentation topic:
 *
 * https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html
 */
public class StockTradesWriter {
    public static void main(String[] args) {
        final String usage = """

                Usage:
                    <streamName>

                Where:
                    streamName - The Amazon Kinesis data stream to which records are written (for example, StockTradeStream)
                """;

        if (args.length != 1) {
            System.out.println(usage);
            System.exit(1);
        }

        String streamName = args[0];
        Region region = Region.US_EAST_1;
        KinesisClient kinesisClient = KinesisClient.builder()
                .region(region)
                .build();

        // Ensure that the Kinesis Stream is valid.
        validateStream(kinesisClient, streamName);
        setStockData(kinesisClient, streamName);
        kinesisClient.close();
    }

    public static void setStockData(KinesisClient kinesisClient, String streamName) {
        try {
            // Repeatedly send stock trades with a 100 milliseconds wait in between.
            StockTradeGenerator stockTradeGenerator = new StockTradeGenerator();

            // Put in 50 Records for this example.
            int index = 50;
            for (int x = 0; x < index; x++) {
                StockTrade trade = stockTradeGenerator.getRandomTrade();
                sendStockTrade(trade, kinesisClient, streamName);
                Thread.sleep(100);
            }

        } catch (KinesisException | InterruptedException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }
        System.out.println("Done");
    }

    private static void sendStockTrade(StockTrade trade, KinesisClient kinesisClient,
            String streamName) {
        byte[] bytes = trade.toJsonAsBytes();

        // The bytes could be null if there is an issue with the JSON serialization by
        // the Jackson JSON library.
        if (bytes == null) {
            System.out.println("Could not get JSON bytes for stock trade");
            return;
        }

        System.out.println("Putting trade: " + trade);
        PutRecordRequest request = PutRecordRequest.builder()
                .partitionKey(trade.getTickerSymbol()) // We use the ticker symbol as the partition key, explained in
                                                       // the Supplemental Information section below.
                .streamName(streamName)
                .data(SdkBytes.fromByteArray(bytes))
                .build();

        try {
            kinesisClient.putRecord(request);
        } catch (KinesisException e) {
            System.err.println(e.getMessage());
        }
    }

    private static void validateStream(KinesisClient kinesisClient, String streamName) {
        try {
            DescribeStreamRequest describeStreamRequest = DescribeStreamRequest.builder()
                    .streamName(streamName)
                    .build();

            DescribeStreamResponse describeStreamResponse = kinesisClient.describeStream(describeStreamRequest);

            if (!describeStreamResponse.streamDescription().streamStatus().toString().equals("ACTIVE")) {
                System.err.println("Stream " + streamName + " is not active. Please wait a few moments and try again.");
                System.exit(1);
            }

        } catch (KinesisException e) {
            System.err.println("Error found while describing the stream " + streamName);
            System.err.println(e);
            System.exit(1);
        }
    }
}
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/master/javav2/example_code/kinesis/src/main/java/com/example/kinesis/StockTradesWriter.java)。

## 使用第三方程式庫
<a name="use-a-third-party-library"></a>

您可以使用其他第三方程式庫，而非實作自訂的訂閱者。此範例示範使用 RxJava 實作，但您可以使用實作反應式串流介面的任何程式庫。如需該程式庫的詳細資訊，請參閱 [Github 上的 RxJava wiki 頁面](https://github.com/ReactiveX/RxJava/wiki)。

若要使用該程式庫，請將其新增做為相依性。如果您使用 Maven，範例會顯示要使用的 POM 片段。

 **POM 項目** 

```
<dependency>
 <groupId>io.reactivex.rxjava2</groupId>
 <artifactId>rxjava</artifactId>
 <version>2.2.21</version>
</dependency>
```

 **匯入** 

```
import java.net.URI;
import java.util.concurrent.CompletableFuture;

import io.reactivex.Flowable;
import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider;
import software.amazon.awssdk.core.async.SdkPublisher;
import software.amazon.awssdk.http.Protocol;
import software.amazon.awssdk.http.SdkHttpConfigurationOption;
import software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.kinesis.KinesisAsyncClient;
import software.amazon.awssdk.services.kinesis.model.ShardIteratorType;
import software.amazon.awssdk.services.kinesis.model.StartingPosition;
import software.amazon.awssdk.services.kinesis.model.SubscribeToShardEvent;
import software.amazon.awssdk.services.kinesis.model.SubscribeToShardRequest;
import software.amazon.awssdk.services.kinesis.model.SubscribeToShardResponseHandler;
import software.amazon.awssdk.utils.AttributeMap;
```

此範例使用 `onEventStream` 生命週期方法中的 RxJava。這讓您能夠完整存取發佈者，後者可用於建立 Rx Flowable。

 **Code** 

```
        SubscribeToShardResponseHandler responseHandler = SubscribeToShardResponseHandler
            .builder()
            .onError(t -> System.err.println("Error during stream - " + t.getMessage()))
            .onEventStream(p -> Flowable.fromPublisher(p)
                                        .ofType(SubscribeToShardEvent.class)
                                        .flatMapIterable(SubscribeToShardEvent::records)
                                        .limit(1000)
                                        .buffer(25)
                                        .subscribe(e -> System.out.println("Record batch = " + e)))
            .build();
```

您也可以使用 `publisherTransformer` 方法搭配 `Flowable` 發佈者。您必須將 `Flowable` 發佈者調整為 *SdkPublisher*，如以下範例所示。

 **Code** 

```
        SubscribeToShardResponseHandler responseHandler = SubscribeToShardResponseHandler
            .builder()
            .onError(t -> System.err.println("Error during stream - " + t.getMessage()))
            .publisherTransformer(p -> SdkPublisher.adapt(Flowable.fromPublisher(p).limit(100)))
            .build();
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/main/javav2/example_code/kinesis/src/main/java/com/example/kinesis/KinesisStreamRxJavaEx.java)。

## 其他資訊
<a name="more-information"></a>
+  Amazon Kinesis API 參考中的 [SubscribeToShardEvent](https://docs.aws.amazon.com/kinesis/latest/APIReference/API_SubscribeToShardEvent.html) 
+  Amazon Kinesis API 參考中的 [SubscribeToShard](https://docs.aws.amazon.com/kinesis/latest/APIReference/API_SubscribeToShard.html) 

# 叫用、列出和刪除 AWS Lambda 函數
<a name="examples-lambda"></a>

本節提供使用 適用於 Java 的 AWS SDK 2.x 搭配 Lambda 服務用戶端進行程式設計的範例。

**Topics**
+ [叫用 Lambda 函數。](#invoke-function)
+ [列出 Lambda 函數](#list-function)
+ [刪除 Lambda 函數](#delete-function)

## 叫用 Lambda 函數。
<a name="invoke-function"></a>

您可以透過建立[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/lambda/LambdaClient.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/lambda/LambdaClient.html)物件並叫用其`invoke`方法來叫用 Lambda 函數。建立 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/lambda/model/InvokeRequest.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/lambda/model/InvokeRequest.html) 物件以指定其他資訊，例如函數名稱和要傳遞給 Lambda 函數的承載。函數名稱會顯示為 *arn：aws：lambda：us-east-1：123456789012：function：HelloFunction*。您可以查看 中的 函數來擷取值 AWS 管理主控台。

若要將承載資料傳遞至函數，請建立包含資訊的[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/SdkBytes.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/SdkBytes.html)物件。例如，在以下的程式碼範例中，請注意傳遞至 Lambda 函數的 JSON 資料。

 **匯入** 

```
import software.amazon.awssdk.services.lambda.LambdaClient;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.lambda.model.InvokeRequest;
import software.amazon.awssdk.core.SdkBytes;
import software.amazon.awssdk.services.lambda.model.InvokeResponse;
import software.amazon.awssdk.services.lambda.model.LambdaException;
```

 **Code** 

下列程式碼範例示範如何叫用 Lambda 函數。

```
    public static void invokeFunction(LambdaClient awsLambda, String functionName) {

         InvokeResponse res = null ;
        try {
            //Need a SdkBytes instance for the payload
            String json = "{\"Hello \":\"Paris\"}";
            SdkBytes payload = SdkBytes.fromUtf8String(json) ;

            //Setup an InvokeRequest
            InvokeRequest request = InvokeRequest.builder()
                    .functionName(functionName)
                    .payload(payload)
                    .build();

            res = awsLambda.invoke(request);
            String value = res.payload().asUtf8String() ;
            System.out.println(value);

        } catch(LambdaException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f807d60010caf3d14fe4cd0801b842fb8e9511ca/javav2/example_code/lambda/src/main/java/com/example/lambda/LambdaInvoke.java)。

## 列出 Lambda 函數
<a name="list-function"></a>

建置`[Lambda Client](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/lambda/LambdaClient.html)`物件並叫用其`listFunctions`方法。此方法會傳回[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/lambda/model/ListFunctionsResponse.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/lambda/model/ListFunctionsResponse.html)物件。您可以叫用此物件的 `functions`方法，以傳回[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/lambda/model/FunctionConfiguration.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/lambda/model/FunctionConfiguration.html)物件清單。您可以逐一查看清單以擷取函數的相關資訊。例如，下列 Java 程式碼範例示範如何取得每個函數名稱。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.lambda.LambdaClient;
import software.amazon.awssdk.services.lambda.model.LambdaException;
import software.amazon.awssdk.services.lambda.model.ListFunctionsResponse;
import software.amazon.awssdk.services.lambda.model.FunctionConfiguration;
import java.util.List;
```

 **Code** 

下列 Java 程式碼範例示範如何擷取函數名稱清單。

```
    public static void listFunctions(LambdaClient awsLambda) {

        try {
            ListFunctionsResponse functionResult = awsLambda.listFunctions();
            List<FunctionConfiguration> list = functionResult.functions();

            for (FunctionConfiguration config: list) {
                System.out.println("The function name is "+config.functionName());
            }

        } catch(LambdaException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f807d60010caf3d14fe4cd0801b842fb8e9511ca/javav2/example_code/lambda/src/main/java/com/example/lambda/ListLambdaFunctions.java)。

## 刪除 Lambda 函數
<a name="delete-function"></a>

建置[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/lambda/LambdaClient.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/lambda/LambdaClient.html)物件並叫用其`deleteFunction`方法。建立[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/lambda/model/DeleteFunctionRequest.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/lambda/model/DeleteFunctionRequest.html)物件並將其傳遞至 `deleteFunction`方法。此物件包含資訊，例如要刪除的函數名稱。函數名稱會顯示為 *arn：aws：lambda：us-east-1：123456789012：function：HelloFunction*。您可以查看 中的 函數來擷取值 AWS 管理主控台。

 **匯入** 

```
import software.amazon.awssdk.services.lambda.LambdaClient;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.lambda.model.DeleteFunctionRequest;
import software.amazon.awssdk.services.lambda.model.LambdaException;
```

 **Code** 

下列 Java 程式碼示範如何刪除 Lambda 函數。

```
    public static void deleteLambdaFunction(LambdaClient awsLambda, String functionName ) {
        try {
            DeleteFunctionRequest request = DeleteFunctionRequest.builder()
                    .functionName(functionName)
                    .build();

            awsLambda.deleteFunction(request);
            System.out.println("The "+functionName +" function was deleted");

        } catch(LambdaException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f807d60010caf3d14fe4cd0801b842fb8e9511ca/javav2/example_code/lambda/src/main/java/com/example/lambda/DeleteFunction.java)。

# 使用 Amazon S3
<a name="examples-s3"></a>

本節提供使用 使用 Amazon S3 的背景資訊 AWS SDK for Java 2.x。本節補充本指南[程式碼範例一節中呈現的 Amazon S3 Java v2](java_s3_code_examples.md) *範例*。

## 中的 S3 用戶端 AWS SDK for Java 2.x
<a name="s3-clients"></a>

 AWS SDK for Java 2.x 提供不同類型的 S3 用戶端。下表顯示差異，並可協助您決定最適合使用案例的方案。


**Amazon S3 用戶端的不同口味**  

| S3 用戶端 | 簡短描述 | 使用情況 | 限制/撤回 | 
| --- | --- | --- | --- | 
|  **AWS CRT 型 S3 用戶端** 介面：[S3AsyncClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClient.html) 建置器：[S3CrtAsyncClientBuilder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClient.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/sdk-for-java/latest/developer-guide/examples-s3.html) 請參閱 [使用高效能 S3 用戶端： AWS CRT 型 S3 用戶端](crt-based-s3-client.md)。  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/sdk-for-java/latest/developer-guide/examples-s3.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/sdk-for-java/latest/developer-guide/examples-s3.html)  | 
|  **啟用分段*的* Java 型 S3 非同步用戶端** 介面：[S3AsyncClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClient.html) Builder：[S3AsyncClientBuilder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClientBuilder.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/sdk-for-java/latest/developer-guide/examples-s3.html) 請參閱 [將 Java 型 S3 非同步用戶端設定為使用平行傳輸](s3-async-client-multipart.md)。  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/sdk-for-java/latest/developer-guide/examples-s3.html)  | 效能低於 AWS CRT 型 S3 用戶端。 | 
|  ***未*啟用分段的 Java 型 S3 非同步用戶端** 介面：[S3AsyncClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClient.html) Builder：[S3AsyncClientBuilder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClientBuilder.html) |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/sdk-for-java/latest/developer-guide/examples-s3.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/sdk-for-java/latest/developer-guide/examples-s3.html)  |  無效能最佳化。  | 
|  **Java 型 S3 同步用戶端** 介面：[S3Client](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3Client.html) Builder：[S3ClientBuilder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3ClientBuilder.html) |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/sdk-for-java/latest/developer-guide/examples-s3.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/sdk-for-java/latest/developer-guide/examples-s3.html)  |  無效能最佳化。  | 

**注意**  
從 2.18.x 版及更新版本開始， AWS SDK for Java 2.x 會在包含端點覆寫時使用[虛擬託管式定址](https://docs.aws.amazon.com/AmazonS3/latest/userguide/VirtualHosting.html#virtual-hosted-style-access)。只要儲存貯體名稱是有效的 DNS 標籤，這就適用。  
在用戶端建置器`true`中使用 呼叫 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3BaseClientBuilder.html#forcePathStyle(java.lang.Boolean](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3BaseClientBuilder.html#forcePathStyle(java.lang.Boolean)方法，強制用戶端使用儲存貯體的路徑樣式定址。  
下列範例顯示使用端點覆寫和路徑樣式定址設定的服務用戶端。  

```
S3Client client = S3Client.builder()
                          .region(Region.US_WEST_2)
                          .endpointOverride(URI.create("https://s3.us-west-2.amazonaws.com"))
                          .forcePathStyle(true)
                          .build();
```

**Topics**
+ [開發套件中的 S3 用戶端](#s3-clients)
+ [將串流上傳至 S3](best-practices-s3-uploads.md)
+ [預先簽章URLs](examples-s3-presign.md)
+ [跨區域存取](s3-cross-region.md)
+ [使用檢查總和保護資料完整性](s3-checksums.md)
+ [使用高效能 S3 用戶端](crt-based-s3-client.md)
+ [設定平行傳輸支援](s3-async-client-multipart.md)
+ [傳輸檔案和目錄](transfer-manager.md)
+ [S3 事件通知](examples-s3-event-notifications.md)

# 使用 將串流上傳至 Amazon S3 AWS SDK for Java 2.x
<a name="best-practices-s3-uploads"></a>

當您使用 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3Client.html#putObject(software.amazon.awssdk.services.s3.model.PutObjectRequest,software.amazon.awssdk.core.sync.RequestBody)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3Client.html#putObject(software.amazon.awssdk.services.s3.model.PutObjectRequest,software.amazon.awssdk.core.sync.RequestBody))或 使用串流將內容上傳至 S3 時[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3Client.html#uploadPart(software.amazon.awssdk.services.s3.model.UploadPartRequest,software.amazon.awssdk.core.sync.RequestBody)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3Client.html#uploadPart(software.amazon.awssdk.services.s3.model.UploadPartRequest,software.amazon.awssdk.core.sync.RequestBody))，您可以使用同步 API 的`RequestBody`原廠類別來提供串流。對於非同步 API， `AsyncRequestBody`是同等的原廠類別。

## 哪些方法上傳串流？
<a name="s3-stream-upload-methods"></a>

對於同步 API，您可以使用下列原廠方法來`RequestBody`提供串流：
+ `[fromInputStream](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/sync/RequestBody.html#fromInputStream(java.io.InputStream,long))(InputStream inputStream, long contentLength)`

  `[fromContentProvider](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/sync/RequestBody.html#fromContentProvider(software.amazon.awssdk.http.ContentStreamProvider,long,java.lang.String))(ContentStreamProvider provider, long contentLength, String mimeType)`
  + `ContentStreamProvider` 具有`fromInputStream(InputStream inputStream)`原廠方法
+ `[fromContentProvider](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/sync/RequestBody.html#fromContentProvider(software.amazon.awssdk.http.ContentStreamProvider,java.lang.String))(ContentStreamProvider provider, String mimeType)`

對於非同步 API，您可以使用 的下列原廠方法`AsyncRequestBody`：
+ `[fromInputStream](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/AsyncRequestBody.html#fromInputStream(java.io.InputStream,java.lang.Long,java.util.concurrent.ExecutorService))(InputStream inputStream, Long contentLength, ExecutorService executor)` 
+ `[fromInputStream](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/AsyncRequestBody.html#fromInputStream(software.amazon.awssdk.core.async.AsyncRequestBodyFromInputStreamConfiguration))(AsyncRequestBodyFromInputStreamConfiguration configuration)`
  + 您可以使用 AsyncRequestBodyFromInputStreamConfiguration.Builder 來提供串流
+ `[fromInputStream](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/AsyncRequestBody.html#fromInputStream(java.util.function.Consumer))(Consumer<AsyncRequestBodyFromInputStreamConfiguration.Builder> configuration)`
+ `[forBlockingInputStream](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/AsyncRequestBody.html#forBlockingInputStream(java.lang.Long))(Long contentLength)`
  + 產生的 `[BlockingInputStreamAsyncRequestBody](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/BlockingInputStreamAsyncRequestBody.html)`包含`writeInputStream(InputStream inputStream)`您可以用來提供串流的方法

## 執行上傳
<a name="s3-upload-stream-perform"></a>

### 如果您知道串流的長度
<a name="s3-stream-upload-supply-content-length"></a>

如先前所示方法的簽章所示，大多數方法都接受內容長度參數。

如果您知道以位元組為單位的內容長度，請提供確切的值：

```
// Always provide the exact content length when it's available.
long contentLength = 1024; // Exact size in bytes.
s3Client.putObject(req -> req
    .bucket("amzn-s3-demo-bucket")
    .key("my-key"),
RequestBody.fromInputStream(inputStream, contentLength));
```

**警告**  
 當您從輸入串流上傳時，如果您指定的內容長度不符合實際的位元組計數，您可能會遇到：  
如果您指定的長度太小，則會截斷物件
如果您指定的長度太大，上傳或掛起連線失敗

### 如果您不知道串流的長度
<a name="s3-stream-upload-unknown-length"></a>

#### 使用同步 API
<a name="s3-upload-unknown-sync-client"></a>

使用 `fromContentProvider(ContentStreamProvider provider, String mimeType)`：

```
public PutObjectResponse syncClient_stream_unknown_size(String bucketName, String key, InputStream inputStream) {

    S3Client s3Client = S3Client.create();

    RequestBody body = RequestBody.fromContentProvider(ContentStreamProvider.fromInputStream(inputStream), "text/plain");
    PutObjectResponse putObjectResponse = s3Client.putObject(b -> b.bucket(BUCKET_NAME).key(KEY_NAME), body);
    return putObjectResponse;
}
```

由於 SDK 會在記憶體中緩衝整個串流以計算內容長度，因此您可以使用大型串流遇到記憶體問題。如果您需要使用同步用戶端上傳大型串流，請考慮使用分段 API：

##### 使用同步用戶端 API 和分段 API 上傳串流
<a name="sync-multipart-upload-stream"></a>

```
public static void uploadStreamToS3(String bucketName, String key, InputStream inputStream) {
    // Create S3 client
    S3Client s3Client = S3Client.create();
    try {
        // Step 1: Initiate the multipart upload
        CreateMultipartUploadRequest createMultipartUploadRequest = CreateMultipartUploadRequest.builder()
                .bucket(bucketName)
                .key(key)
                .build();

        CreateMultipartUploadResponse createResponse = s3Client.createMultipartUpload(createMultipartUploadRequest);
        String uploadId = createResponse.uploadId();
        System.out.println("Started multipart upload with ID: " + uploadId);

        // Step 2: Upload parts
        List<CompletedPart> completedParts = new ArrayList<>();
        int partNumber = 1;
        byte[] buffer = new byte[PART_SIZE];
        int bytesRead;

        try {
            while ((bytesRead = readFullyOrToEnd(inputStream, buffer)) > 0) {
                // Create request to upload a part
                UploadPartRequest uploadPartRequest = UploadPartRequest.builder()
                        .bucket(bucketName)
                        .key(key)
                        .uploadId(uploadId)
                        .partNumber(partNumber)
                        .build();

                // If we didn't read a full buffer, create a properly sized byte array
                RequestBody requestBody;
                if (bytesRead < PART_SIZE) {
                    byte[] lastPartBuffer = new byte[bytesRead];
                    System.arraycopy(buffer, 0, lastPartBuffer, 0, bytesRead);
                    requestBody = RequestBody.fromBytes(lastPartBuffer);
                } else {
                    requestBody = RequestBody.fromBytes(buffer);
                }

                // Upload the part and save the response's ETag
                UploadPartResponse uploadPartResponse = s3Client.uploadPart(uploadPartRequest, requestBody);
                CompletedPart part = CompletedPart.builder()
                        .partNumber(partNumber)
                        .eTag(uploadPartResponse.eTag())
                        .build();
                completedParts.add(part);

                System.out.println("Uploaded part " + partNumber + " with size " + bytesRead + " bytes");
                partNumber++;
            }

            // Step 3: Complete the multipart upload
            CompletedMultipartUpload completedMultipartUpload = CompletedMultipartUpload.builder()
                    .parts(completedParts)
                    .build();

            CompleteMultipartUploadRequest completeRequest = CompleteMultipartUploadRequest.builder()
                    .bucket(bucketName)
                    .key(key)
                    .uploadId(uploadId)
                    .multipartUpload(completedMultipartUpload)
                    .build();

            CompleteMultipartUploadResponse completeResponse = s3Client.completeMultipartUpload(completeRequest);
            System.out.println("Multipart upload completed. Object URL: " + completeResponse.location());

        } catch (Exception e) {
            // If an error occurs, abort the multipart upload
            System.err.println("Error during multipart upload: " + e.getMessage());
            AbortMultipartUploadRequest abortRequest = AbortMultipartUploadRequest.builder()
                    .bucket(bucketName)
                    .key(key)
                    .uploadId(uploadId)
                    .build();
            s3Client.abortMultipartUpload(abortRequest);
            System.err.println("Multipart upload aborted");
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                System.err.println("Error closing input stream: " + e.getMessage());
            }
        }
    } finally {
        s3Client.close();
    }
}

/**
 * Reads from the input stream into the buffer, attempting to fill the buffer completely
 * or until the end of the stream is reached.
 *
 * @param inputStream the input stream to read from
 * @param buffer      the buffer to fill
 * @return the number of bytes read, or -1 if the end of the stream is reached before any bytes are read
 * @throws IOException if an I/O error occurs
 */
private static int readFullyOrToEnd(InputStream inputStream, byte[] buffer) throws IOException {
    int totalBytesRead = 0;
    int bytesRead;

    while (totalBytesRead < buffer.length) {
        bytesRead = inputStream.read(buffer, totalBytesRead, buffer.length - totalBytesRead);
        if (bytesRead == -1) {
            break; // End of stream
        }
        totalBytesRead += bytesRead;
    }

    return totalBytesRead > 0 ? totalBytesRead : -1;
}
```

**注意**  
對於大多數使用案例，我們建議針對大小不明的串流使用非同步用戶端 API。這種方法可以實現平行傳輸並提供更簡單的程式設計界面，因為如果串流很大，開發套件會將串流分割處理為分段區塊。  
啟用分段的標準 S3 非同步用戶端和 CRT 型 S3 AWS 用戶端都會實作此方法。我們在下一節中顯示此方法的範例。

#### 使用非同步 API
<a name="s3-stream-upload-unknown-async-client"></a>

您可以將 `contentLength`引數`null`提供給 `fromInputStream(InputStream inputStream, Long contentLength, ExecutorService executor)`

**Example 使用以 AWS CRT 為基礎的非同步用戶端：**  

```
public PutObjectResponse crtClient_stream_unknown_size(String bucketName, String key, InputStream inputStream) {

    S3AsyncClient s3AsyncClient = S3AsyncClient.crtCreate();
    ExecutorService executor = Executors.newSingleThreadExecutor();
    AsyncRequestBody body = AsyncRequestBody.fromInputStream(inputStream, null, executor);  // 'null' indicates that the
                                                                                            // content length is unknown.
    CompletableFuture<PutObjectResponse> responseFuture =
            s3AsyncClient.putObject(r -> r.bucket(bucketName).key(key), body)
                    .exceptionally(e -> {
                        if (e != null){
                            logger.error(e.getMessage(), e);
                        }
                        return null;
                    });

    PutObjectResponse response = responseFuture.join(); // Wait for the response.
    executor.shutdown();
    return response;
}
```

**Example 使用啟用分段的標準非同步用戶端：**  

```
public PutObjectResponse asyncClient_multipart_stream_unknown_size(String bucketName, String key, InputStream inputStream) {

    S3AsyncClient s3AsyncClient = S3AsyncClient.builder().multipartEnabled(true).build();
    ExecutorService executor = Executors.newSingleThreadExecutor();
    AsyncRequestBody body = AsyncRequestBody.fromInputStream(inputStream, null, executor); // 'null' indicates that the
                                                                                           // content length is unknown.
    CompletableFuture<PutObjectResponse> responseFuture =
            s3AsyncClient.putObject(r -> r.bucket(bucketName).key(key), body)
                    .exceptionally(e -> {
                        if (e != null) {
                            logger.error(e.getMessage(), e);
                        }
                        return null;
                    });

    PutObjectResponse response = responseFuture.join(); // Wait for the response.
    executor.shutdown();
    return response;
}
```

# 使用 Amazon S3 預先簽章URLs
<a name="examples-s3-presign"></a>

預先簽章URLs 提供私有 S3 物件的暫時存取權，而不需要使用者擁有 AWS 登入資料或許可。

例如，假設 Alice 可以存取 S3 物件，而且她想要暫時與 Bob 共用該物件的存取權。Alice 可以產生預先簽章的 GET 請求來與 Bob 共用，以便他可以下載物件，而無需存取 Alice 的登入資料。您可以為 HTTP GET 產生預先簽章URLs，並為 HTTP PUT 請求產生預先簽章的 URL。

## 產生物件的預先簽章 URL，然後下載它 (GET 請求）
<a name="get-presignedobject"></a>

下列範例包含兩個部分。
+ 第 1 部分：Alice 會產生物件的預先簽章 URL。
+ 第 2 部分：Bob 使用預先簽章的 URL 下載物件。

### 第 1 部分：產生 URL
<a name="get-presigned-object-part1"></a>

Alice 在 S3 儲存貯體中已有物件。她使用以下程式碼來產生可在後續 GET 請求中使用的 URL 字串。

#### 匯入
<a name="get-presigned-example-imports"></a>

```
import com.example.s3.util.PresignUrlUtils;
import org.slf4j.Logger;
import software.amazon.awssdk.http.HttpExecuteRequest;
import software.amazon.awssdk.http.HttpExecuteResponse;
import software.amazon.awssdk.http.SdkHttpClient;
import software.amazon.awssdk.http.SdkHttpMethod;
import software.amazon.awssdk.http.SdkHttpRequest;
import software.amazon.awssdk.http.apache.ApacheHttpClient;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.S3Exception;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest;
import software.amazon.awssdk.services.s3.presigner.model.PresignedGetObjectRequest;
import software.amazon.awssdk.utils.IoUtils;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.UUID;
```

```
    /* Create a pre-signed URL to download an object in a subsequent GET request. */
    public String createPresignedGetUrl(String bucketName, String keyName) {
        try (S3Presigner presigner = S3Presigner.create()) {

            GetObjectRequest objectRequest = GetObjectRequest.builder()
                    .bucket(bucketName)
                    .key(keyName)
                    .build();

            GetObjectPresignRequest presignRequest = GetObjectPresignRequest.builder()
                    .signatureDuration(Duration.ofMinutes(10))  // The URL will expire in 10 minutes.
                    .getObjectRequest(objectRequest)
                    .build();

            PresignedGetObjectRequest presignedRequest = presigner.presignGetObject(presignRequest);
            logger.info("Presigned URL: [{}]", presignedRequest.url().toString());
            logger.info("HTTP method: [{}]", presignedRequest.httpRequest().method());

            return presignedRequest.url().toExternalForm();
        }
    }
```

### 第 2 部分：下載 物件
<a name="get-presigned-object-part2"></a>

Bob 使用下列三個程式碼選項之一來下載物件。或者，他可以使用瀏覽器來執行 GET 請求。

#### 使用 JDK `HttpURLConnection`（自 1.1 版起）
<a name="get-presigned-example-useHttpUrlConnection"></a>

```
    /* Use the JDK HttpURLConnection (since v1.1) class to do the download. */
    public byte[] useHttpUrlConnectionToGet(String presignedUrlString) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); // Capture the response body to a byte array.

        try {
            URL presignedUrl = new URL(presignedUrlString);
            HttpURLConnection connection = (HttpURLConnection) presignedUrl.openConnection();
            connection.setRequestMethod("GET");
            // Download the result of executing the request.
            try (InputStream content = connection.getInputStream()) {
                IoUtils.copy(content, byteArrayOutputStream);
            }
            logger.info("HTTP response code is " + connection.getResponseCode());

        } catch (S3Exception | IOException e) {
            logger.error(e.getMessage(), e);
        }
        return byteArrayOutputStream.toByteArray();
    }
```

#### 使用 JDK `HttpClient`（自 v11 起）
<a name="get-presigned-example-useHttpClient"></a>

```
    /* Use the JDK HttpClient (since v11) class to do the download. */
    public byte[] useHttpClientToGet(String presignedUrlString) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); // Capture the response body to a byte array.

        HttpRequest.Builder requestBuilder = HttpRequest.newBuilder();
        HttpClient httpClient = HttpClient.newHttpClient();
        try {
            URL presignedUrl = new URL(presignedUrlString);
            HttpResponse<InputStream> response = httpClient.send(requestBuilder
                            .uri(presignedUrl.toURI())
                            .GET()
                            .build(),
                    HttpResponse.BodyHandlers.ofInputStream());

            IoUtils.copy(response.body(), byteArrayOutputStream);

            logger.info("HTTP response code is " + response.statusCode());

        } catch (URISyntaxException | InterruptedException | IOException e) {
            logger.error(e.getMessage(), e);
        }
        return byteArrayOutputStream.toByteArray();
    }
```

#### `SdkHttpClient` 從適用於 Java 的 開發套件使用
<a name="get-presigned-example-useSdkHttpClient"></a>

```
    /* Use the AWS SDK for Java SdkHttpClient class to do the download. */
    public byte[] useSdkHttpClientToGet(String presignedUrlString) {

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); // Capture the response body to a byte array.
        try {
            URL presignedUrl = new URL(presignedUrlString);
            SdkHttpRequest request = SdkHttpRequest.builder()
                    .method(SdkHttpMethod.GET)
                    .uri(presignedUrl.toURI())
                    .build();

            HttpExecuteRequest executeRequest = HttpExecuteRequest.builder()
                    .request(request)
                    .build();

            try (SdkHttpClient sdkHttpClient = ApacheHttpClient.create()) {
                HttpExecuteResponse response = sdkHttpClient.prepareRequest(executeRequest).call();
                response.responseBody().ifPresentOrElse(
                        abortableInputStream -> {
                            try {
                                IoUtils.copy(abortableInputStream, byteArrayOutputStream);
                            } catch (IOException e) {
                                throw new RuntimeException(e);
                            }
                        },
                        () -> logger.error("No response body."));

                logger.info("HTTP Response code is {}", response.httpResponse().statusCode());
            }
        } catch (URISyntaxException | IOException e) {
            logger.error(e.getMessage(), e);
        }
        return byteArrayOutputStream.toByteArray();
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/d73001daea05266eaa9e074ccb71b9383832369a/javav2/example_code/s3/src/main/java/com/example/s3/GeneratePresignedGetUrlAndRetrieve.java)和[測試](https://github.com/awsdocs/aws-doc-sdk-examples/blob/d73001daea05266eaa9e074ccb71b9383832369a/javav2/example_code/s3/src/test/java/com/example/s3/presignurl/GeneratePresignedGetUrlTests.java)。

## 產生上傳的預先簽章 URL，然後上傳檔案 (PUT 請求）
<a name="put-presignedobject"></a>

下列範例包含兩個部分。
+ 第 1 部分：Alice 會產生預先簽章的 URL 來上傳物件。
+ 第 2 部分：Bob 使用預先簽章的 URL 上傳檔案。

### 第 1 部分：產生 URL
<a name="put-presigned-object-part1"></a>

Alice 已有 S3 儲存貯體。她使用以下程式碼來產生可在後續 PUT 請求中使用的 URL 字串。

#### 匯入
<a name="put-presigned-example-imports"></a>

```
import com.example.s3.util.PresignUrlUtils;
import org.slf4j.Logger;
import software.amazon.awssdk.core.internal.sync.FileContentStreamProvider;
import software.amazon.awssdk.http.HttpExecuteRequest;
import software.amazon.awssdk.http.HttpExecuteResponse;
import software.amazon.awssdk.http.SdkHttpClient;
import software.amazon.awssdk.http.SdkHttpMethod;
import software.amazon.awssdk.http.SdkHttpRequest;
import software.amazon.awssdk.http.apache.ApacheHttpClient;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.S3Exception;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
import software.amazon.awssdk.services.s3.presigner.model.PresignedPutObjectRequest;
import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.Map;
import java.util.UUID;
```

```
    /* Create a presigned URL to use in a subsequent PUT request */
    public String createPresignedUrl(String bucketName, String keyName, Map<String, String> metadata) {
        try (S3Presigner presigner = S3Presigner.create()) {

            PutObjectRequest objectRequest = PutObjectRequest.builder()
                    .bucket(bucketName)
                    .key(keyName)
                    .metadata(metadata)
                    .build();

            PutObjectPresignRequest presignRequest = PutObjectPresignRequest.builder()
                    .signatureDuration(Duration.ofMinutes(10))  // The URL expires in 10 minutes.
                    .putObjectRequest(objectRequest)
                    .build();


            PresignedPutObjectRequest presignedRequest = presigner.presignPutObject(presignRequest);
            String myURL = presignedRequest.url().toString();
            logger.info("Presigned URL to upload a file to: [{}]", myURL);
            logger.info("HTTP method: [{}]", presignedRequest.httpRequest().method());

            return presignedRequest.url().toExternalForm();
        }
    }
```

### 第 2 部分：上傳檔案物件
<a name="put-presigned-object-part2"></a>

Bob 使用下列三個程式碼選項之一來上傳檔案。

#### 使用 JDK `HttpURLConnection`（自 1.1 版起）
<a name="put-presigned-example-useHttpUrlConnection"></a>

```
    /* Use the JDK HttpURLConnection (since v1.1) class to do the upload. */
    public void useHttpUrlConnectionToPut(String presignedUrlString, File fileToPut, Map<String, String> metadata) {
        logger.info("Begin [{}] upload", fileToPut.toString());
        try {
            URL presignedUrl = new URL(presignedUrlString);
            HttpURLConnection connection = (HttpURLConnection) presignedUrl.openConnection();
            connection.setDoOutput(true);
            metadata.forEach((k, v) -> connection.setRequestProperty("x-amz-meta-" + k, v));
            connection.setRequestMethod("PUT");
            OutputStream out = connection.getOutputStream();

            try (RandomAccessFile file = new RandomAccessFile(fileToPut, "r");
                 FileChannel inChannel = file.getChannel()) {
                ByteBuffer buffer = ByteBuffer.allocate(8192); //Buffer size is 8k

                while (inChannel.read(buffer) > 0) {
                    buffer.flip();
                    for (int i = 0; i < buffer.limit(); i++) {
                        out.write(buffer.get());
                    }
                    buffer.clear();
                }
            } catch (IOException e) {
                logger.error(e.getMessage(), e);
            }

            out.close();
            connection.getResponseCode();
            logger.info("HTTP response code is " + connection.getResponseCode());

        } catch (S3Exception | IOException e) {
            logger.error(e.getMessage(), e);
        }
    }
```

#### 使用 JDK `HttpClient`（自 v11 起）
<a name="put-presigned-example-useHttpClient"></a>

```
    /* Use the JDK HttpClient (since v11) class to do the upload. */
    public void useHttpClientToPut(String presignedUrlString, File fileToPut, Map<String, String> metadata) {
        logger.info("Begin [{}] upload", fileToPut.toString());

        HttpRequest.Builder requestBuilder = HttpRequest.newBuilder();
        metadata.forEach((k, v) -> requestBuilder.header("x-amz-meta-" + k, v));

        HttpClient httpClient = HttpClient.newHttpClient();
        try {
            final HttpResponse<Void> response = httpClient.send(requestBuilder
                            .uri(new URL(presignedUrlString).toURI())
                            .PUT(HttpRequest.BodyPublishers.ofFile(Path.of(fileToPut.toURI())))
                            .build(),
                    HttpResponse.BodyHandlers.discarding());

            logger.info("HTTP response code is " + response.statusCode());

        } catch (URISyntaxException | InterruptedException | IOException e) {
            logger.error(e.getMessage(), e);
        }
    }
```

#### `SdkHttpClient` 從適用於 Java 的 開發套件使用
<a name="put-presigned-example-useSdkHttpClient"></a>

```
    /* Use the AWS SDK for Java V2 SdkHttpClient class to do the upload. */
    public void useSdkHttpClientToPut(String presignedUrlString, File fileToPut, Map<String, String> metadata) {
        logger.info("Begin [{}] upload", fileToPut.toString());

        try {
            URL presignedUrl = new URL(presignedUrlString);

            SdkHttpRequest.Builder requestBuilder = SdkHttpRequest.builder()
                    .method(SdkHttpMethod.PUT)
                    .uri(presignedUrl.toURI());
            // Add headers
            metadata.forEach((k, v) -> requestBuilder.putHeader("x-amz-meta-" + k, v));
            // Finish building the request.
            SdkHttpRequest request = requestBuilder.build();

            HttpExecuteRequest executeRequest = HttpExecuteRequest.builder()
                    .request(request)
                    .contentStreamProvider(new FileContentStreamProvider(fileToPut.toPath()))
                    .build();

            try (SdkHttpClient sdkHttpClient = ApacheHttpClient.create()) {
                HttpExecuteResponse response = sdkHttpClient.prepareRequest(executeRequest).call();
                logger.info("Response code: {}", response.httpResponse().statusCode());
            }
        } catch (URISyntaxException | IOException e) {
            logger.error(e.getMessage(), e);
        }
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/d73001daea05266eaa9e074ccb71b9383832369a/javav2/example_code/s3/src/main/java/com/example/s3/GeneratePresignedUrlAndPutFileWithMetadata.java)和[測試](https://github.com/awsdocs/aws-doc-sdk-examples/blob/d73001daea05266eaa9e074ccb71b9383832369a/javav2/example_code/s3/src/test/java/com/example/s3/presignurl/GeneratePresignedPutUrlTests.java)。

# Amazon S3 的跨區域存取
<a name="s3-cross-region"></a>

當您使用 Amazon Simple Storage Service (Amazon S3) 儲存貯體時，您通常知道儲存貯體 AWS 區域 的 。您使用的區域會在您建立 S3 用戶端時決定。

不過，有時您可能需要使用特定儲存貯體，但您不知道它是否位於為 S3 用戶端設定的相同區域中。

您可以使用 SDK 來啟用跨不同區域的 S3 儲存貯體存取權，而不是進行更多呼叫來判斷儲存貯體區域。

## 設定
<a name="s3-cross-region-setup"></a>

跨區域存取的支援隨 SDK `2.20.111` 版本提供。在 Maven 建置檔案中使用此版本或更新版本做為`s3`相依性，如下列程式碼片段所示。

```
<dependency>
  <groupId>software.amazon.awssdk</groupId>
  <artifactId>s3</artifactId>
  <version>2.27.21</version>
</dependency>
```

接著，當您建立 S3 用戶端時，請啟用跨區域存取，如程式碼片段所示。根據預設，不會啟用存取。

```
S3AsyncClient client = S3AsyncClient.builder()
                                    .crossRegionAccessEnabled(true)
                                    .build();
```

## 開發套件如何提供跨區域存取
<a name="s3-cross-region-routing"></a>

當您在請求中參考現有的儲存貯體時，例如當您使用 `putObject`方法時，軟體開發套件會對為用戶端設定的 區域啟動請求。

如果儲存貯體不存在於該特定區域中，則錯誤回應會包含儲存貯體所在的實際區域。開發套件接著會在第二個請求中使用正確的區域。

為了最佳化對相同儲存貯體的未來請求，開發套件會在用戶端中快取此區域映射。

## 考量事項
<a name="s3-cross-region-considerations"></a>

當您啟用跨區域儲存貯體存取時，請注意，如果儲存貯體不在用戶端設定的區域中，第一個 API 呼叫可能會導致延遲增加。不過，後續呼叫會受益於快取的區域資訊，進而改善效能。

當您啟用跨區域存取時，對儲存貯體的存取不會受到影響。使用者必須獲得授權，才能在其所在的任何區域中存取儲存貯體。

# 使用檢查總和保護資料完整性
<a name="s3-checksums"></a>

Amazon Simple Storage Service (Amazon S3) 可讓您在上傳物件時指定檢查總和。當您指定檢查總和時，它會與 物件一起存放，並且可以在下載物件時進行驗證。

當您傳輸檔案時，檢查總和可提供多一層的資料完整性。使用檢查總和，您可以透過確認收到的檔案符合原始檔案來驗證資料一致性。如需使用 Amazon S3 檢查總和的詳細資訊，請參閱 [Amazon Simple Storage Service 使用者指南](https://docs.aws.amazon.com/AmazonS3/latest/userguide/checking-object-integrity.html)，包括[支援的演算法](https://docs.aws.amazon.com/AmazonS3/latest/userguide/checking-object-integrity.html#using-additional-checksums)。

您可以靈活地選擇最符合您需求的演算法，並讓 SDK 計算檢查總和。或者，您可以使用其中一個支援的演算法來提供預先計算的檢查總和值。

**注意**  
從 2.30.0 版開始 AWS SDK for Java 2.x，開發套件會自動計算上傳的`CRC32`檢查總和，以提供預設完整性保護。如果您未提供預先計算的檢查總和值，或者您未指定 SDK 應該用來計算檢查總和的演算法，則 SDK 會計算此檢查總和。  
軟體開發套件還提供全域設定，用於外部設定的資料完整性保護，您可以在軟體[AWS SDKs和工具參考指南](https://docs.aws.amazon.com/sdkref/latest/guide/feature-dataintegrity.html)中閱讀這些保護。

我們討論兩個請求階段的檢查總和：上傳物件和下載物件。

## 上傳物件
<a name="use-service-S3-checksum-upload"></a>

當您使用 `putObject`方法上傳物件並提供檢查總和演算法時，開發套件會計算指定演算法的檢查總和。

下列程式碼片段顯示使用`SHA256`檢查總和上傳物件的請求。當 SDK 傳送請求時，它會計算`SHA256`檢查總和並上傳物件。Amazon S3 透過計算檢查總和並將其與 SDK 提供的檢查總和進行比較，來驗證內容的完整性。然後，Amazon S3 會將檢查總和與 物件一起存放。

```
public void putObjectWithChecksum() {
        s3Client.putObject(b -> b
                .bucket(bucketName)
                .key(key)
                .checksumAlgorithm(ChecksumAlgorithm.SHA256),
            RequestBody.fromString("This is a test"));
}
```

如果您未隨請求提供檢查總和演算法，檢查總和行為會根據您使用的 SDK 版本而有所不同，如下表所示。

**未提供檢查總和演算法時的檢查總和行為**


| Java 開發套件版本 | 檢查總和行為 | 
| --- | --- | 
| 早於 2.30.0 | 軟體開發套件不會自動計算以 CRC 為基礎的檢查總和，並在請求中提供它。 | 
| 2.30.0 或更新版本 | SDK 使用`CRC32`演算法來計算檢查總和，並在請求中提供檢查總和。Amazon S3 透過計算自己的`CRC32`檢查總和來驗證傳輸的完整性，並將其與 SDK 提供的檢查總和進行比較。如果檢查總和相符，檢查總和會與 物件一起儲存。 | 

### 使用預先計算的檢查總和值
<a name="use-service-S3-checksum-upload-pre"></a>

隨請求提供的預先計算檢查總和值會停用 SDK 的自動運算，並改用提供的值。

下列範例顯示具有預先計算 SHA256 檢查總和的請求。

```
    public void putObjectWithPrecalculatedChecksum(String filePath) {
        String checksum = calculateChecksum(filePath, "SHA-256");

        s3Client.putObject((b -> b
                .bucket(bucketName)
                .key(key)
                .checksumSHA256(checksum)),
            RequestBody.fromFile(Paths.get(filePath)));
    }
```

如果 Amazon S3 判斷指定演算法的檢查總和值不正確，則服務會傳回錯誤回應。

### 分段上傳
<a name="use-service-S3-checksum-upload-multi"></a>

您也可以使用具有分段上傳的檢查總和。

 適用於 Java 的 SDK 2.x 提供兩個選項，以使用具有分段上傳的檢查總和。第一個選項使用 `S3TransferManager`。

下列 Transfer Manager 範例會指定上傳的 SHA1 演算法。

```
    public void multipartUploadWithChecksumTm(String filePath) {
        S3TransferManager transferManager = S3TransferManager.create();
        UploadFileRequest uploadFileRequest = UploadFileRequest.builder()
            .putObjectRequest(b -> b
                .bucket(bucketName)
                .key(key)
                .checksumAlgorithm(ChecksumAlgorithm.SHA1))
            .source(Paths.get(filePath))
            .build();
        FileUpload fileUpload = transferManager.uploadFile(uploadFileRequest);
        fileUpload.completionFuture().join();
        transferManager.close();
    }
```

如果您在使用傳輸管理員進行上傳時未提供檢查總和演算法，軟體開發套件會根據`CRC32`演算法自動計算和檢查總和。軟體開發套件會針對所有版本的軟體開發套件執行此計算。

第二個選項使用 [`S3Client` API](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3Client.html) （或 [`S3AsyncClient` API](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClient.html)) 來執行分段上傳。如果您使用此方法指定檢查總和，則必須指定啟動上傳時要使用的演算法。您還必須為每一個分段請求指定演算法，並在每一個分段上傳後提供為其計算的總和檢查。

```
    public void multipartUploadWithChecksumS3Client(String filePath) {
        ChecksumAlgorithm algorithm = ChecksumAlgorithm.CRC32;

        // Initiate the multipart upload.
        CreateMultipartUploadResponse createMultipartUploadResponse = s3Client.createMultipartUpload(b -> b
            .bucket(bucketName)
            .key(key)
            .checksumAlgorithm(algorithm)); // Checksum specified on initiation.
        String uploadId = createMultipartUploadResponse.uploadId();

        // Upload the parts of the file.
        int partNumber = 1;
        List<CompletedPart> completedParts = new ArrayList<>();
        ByteBuffer bb = ByteBuffer.allocate(1024 * 1024 * 5); // 5 MB byte buffer

        try (RandomAccessFile file = new RandomAccessFile(filePath, "r")) {
            long fileSize = file.length();
            long position = 0;
            while (position < fileSize) {
                file.seek(position);
                long read = file.getChannel().read(bb);

                bb.flip(); // Swap position and limit before reading from the buffer.
                UploadPartRequest uploadPartRequest = UploadPartRequest.builder()
                    .bucket(bucketName)
                    .key(key)
                    .uploadId(uploadId)
                    .checksumAlgorithm(algorithm) // Checksum specified on each part.
                    .partNumber(partNumber)
                    .build();

                UploadPartResponse partResponse = s3Client.uploadPart(
                    uploadPartRequest,
                    RequestBody.fromByteBuffer(bb));

                CompletedPart part = CompletedPart.builder()
                    .partNumber(partNumber)
                    .checksumCRC32(partResponse.checksumCRC32()) // Provide the calculated checksum.
                    .eTag(partResponse.eTag())
                    .build();
                completedParts.add(part);

                bb.clear();
                position += read;
                partNumber++;
            }
        } catch (IOException e) {
            System.err.println(e.getMessage());
        }

        // Complete the multipart upload.
        s3Client.completeMultipartUpload(b -> b
            .bucket(bucketName)
            .key(key)
            .uploadId(uploadId)
            .multipartUpload(CompletedMultipartUpload.builder().parts(completedParts).build()));
    }
```

[完整範例和測試的程式碼](https://github.com/awsdocs/aws-doc-sdk-examples/blob/main/javav2/example_code/s3/src/main/java/com/example/s3/PerformMultiPartUpload.java)位於 GitHub 程式碼範例儲存庫中。 [https://github.com/awsdocs/aws-doc-sdk-examples/blob/main/javav2/example_code/s3/src/test/java/com/example/s3/PerformMultiPartUploadTests.java](https://github.com/awsdocs/aws-doc-sdk-examples/blob/main/javav2/example_code/s3/src/test/java/com/example/s3/PerformMultiPartUploadTests.java)

## 下載物件
<a name="use-service-S3-checksum-download"></a>

當您使用 [getObject](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3Client.html#getObject(software.amazon.awssdk.services.s3.model.GetObjectRequest)) getObject方法下載物件時，開發套件會在當 的建置器 `checksumMode`方法`GetObjectRequest`設定為 時`ChecksumMode.ENABLED`。

以下程式碼片段中的請求會指示 SDK 透過計算檢查總和並比較值來驗證回應中的檢查總和。

```
    public GetObjectResponse getObjectWithChecksum() {
        return s3Client.getObject(b -> b
                        .bucket(bucketName)
                        .key(key)
                        .checksumMode(ChecksumMode.ENABLED))
                .response();
    }
```

**注意**  
如果物件未使用檢查總和上傳，則不會進行驗證。

## 其他檢查總和計算選項
<a name="S3-checsum-calculation-options"></a>

**注意**  
為了驗證傳輸資料的資料完整性並識別任何傳輸錯誤，我們建議使用者保留檢查總和計算選項的 SDK 預設設定。根據預設，軟體開發套件會針對許多 S3 操作新增此重要檢查，包括 `PutObject`和 `GetObject`。

不過，如果您使用 Amazon S3 需要最少的檢查總和驗證，您可以透過變更預設組態設定來停用許多檢查。

### 除非需要，否則停用自動檢查總和計算
<a name="S3-minimize-checksum-calc-global"></a>

您可以針對支援 SDK 的操作停用 SDK 的自動檢查總和計算，例如 `PutObject`和 `GetObject`。不過，某些 S3 操作需要檢查總和計算；您無法停用這些操作的檢查總和計算。

開發套件針對請求的承載和回應的承載，提供計算檢查總和的個別設定。

下列清單說明您可以用來將不同範圍的檢查總和計算降至最低的設定。
+ **所有應用程式範圍** - 透過變更環境變數中的設定或共用 AWS `config`和`credentials`檔案中的設定檔，所有應用程式都可以使用這些設定。除非在應用程式或服務用戶端範圍遭到覆寫，否則這些設定會影響所有 AWS SDK 應用程式中的所有服務用戶端。
  + 在設定檔中新增設定：

    ```
    [default]
    request_checksum_calculation = WHEN_REQUIRED
    response_checksum_validation = WHEN_REQUIRED
    ```
  + 新增環境變數：

    ```
    AWS_REQUEST_CHECKSUM_CALCULATION=WHEN_REQUIRED
    AWS_RESPONSE_CHECKSUM_VALIDATION=WHEN_REQUIRED
    ```
+ **目前的應用程式範圍** - 您可以將 Java 系統屬性設定為 `aws.requestChecksumCalculation``WHEN_REQUIRED`，以限制檢查總和計算。回應的對應系統屬性為 `aws.responseChecksumValidation`。

  除非在建立服務用戶端期間遭到覆寫，否則這些設定會影響應用程式中的所有 SDK 服務用戶端。

  在應用程式開始時設定系統屬性：

  ```
  import software.amazon.awssdk.core.SdkSystemSetting;
  import software.amazon.awssdk.core.checksums.RequestChecksumCalculation;
  import software.amazon.awssdk.core.checksums.ResponseChecksumValidation;
  import software.amazon.awssdk.services.s3.S3Client;
  
  class DemoClass {
      public static void main(String[] args) {
  
          System.setProperty(SdkSystemSetting.AWS_REQUEST_CHECKSUM_CALCULATION.property(), // Resolves to "aws.requestChecksumCalculation".
                  "WHEN_REQUIRED");
          System.setProperty(SdkSystemSetting.AWS_RESPONSE_CHECKSUM_VALIDATION.property(), // Resolves to "aws.responseChecksumValidation".
                  "WHEN_REQUIRED");
  
          S3Client s3Client = S3Client.builder().build();
  
          // Use s3Client.
      }
  }
  ```
+ **單一 S3 服務用戶端範圍** - 您可以使用建置器方法，設定單一 S3 服務用戶端來計算檢查總和的最小數量：

  ```
  import software.amazon.awssdk.core.checksums.RequestChecksumCalculation;
  import software.amazon.awssdk.services.s3.S3Client;
  
  public class RequiredChecksums {
      public static void main(String[] args) {
          S3Client s3 = S3Client.builder()
                  .requestChecksumCalculation(RequestChecksumCalculation.WHEN_REQUIRED)
                  .responseChecksumValidation(ResponseChecksumValidation.WHEN_REQUIRED)
                  .build();
  
          // Use s3Client. 
      }
  // ...
  }
  ```

### 使用 `LegacyMd5Plugin`以簡化 MD5 相容性
<a name="S3-checksum-legacy-md5"></a>

除了發行 2.30.0 版的 CRC32 檢查總和行為之外，SDK 也停止計算必要操作的 MD5 檢查總和。

如果您需要 S3 操作的舊版 MD5 檢查總和行為，您可以使用 `LegacyMd5Plugin`，該 已隨 SDK 的 2.31.32 版發行。

當您需要與依賴舊版 MD5 檢查總和行為的應用程式保持相容性時，`LegacyMd5Plugin`特別有用，特別是在與第三方 S3-compatible儲存提供者搭配使用時，例如與 S3A 檔案系統連接器 (Apache Spark、Iceberg) 搭配使用時。

若要使用 `LegacyMd5Plugin`，請將其新增至 S3 用戶端建置器：

```
// For synchronous S3 client.
S3Client s3Client = S3Client.builder()
                           .addPlugin(LegacyMd5Plugin.create())
                           .build();

// For asynchronous S3 client.
S3AsyncClient asyncClient = S3AsyncClient.builder()
                                       .addPlugin(LegacyMd5Plugin.create())
                                       .build();
```

如果您想要將 MD5 檢查總和新增至需要檢查總和的操作，並想要略過為支援檢查總和但非必要的操作新增 SDK 預設檢查總和，您可以啟用`ClientBuilder`選項 `requestChecksumCalculation`和 `responseChecksumValidation`作為 `WHEN_REQUIRED`。這只會將 SDK 預設檢查總和新增至需要檢查總和的操作：

```
// Use the `LegacyMd5Plugin` with `requestChecksumCalculation` and `responseChecksumValidation` set to WHEN_REQUIRED.
S3AsyncClient asyncClient = S3AsyncClient.builder()
                                       .addPlugin(LegacyMd5Plugin.create())
                                       .requestChecksumCalculation(RequestChecksumCalculation.WHEN_REQUIRED)
                                       .responseChecksumValidation(ResponseChecksumValidation.WHEN_REQUIRED)
                                       .build();
```

此組態在使用可能無法完全支援較新檢查總和演算法，但仍需要特定操作的 MD5 檢查總和的第三方 S3-compatible儲存系統時特別有用。

# 使用高效能 S3 用戶端： AWS CRT 型 S3 用戶端
<a name="crt-based-s3-client"></a>

 AWS CRT 型 S3 用戶端，建置在[AWS 通用執行期 (CRT)](https://docs.aws.amazon.com/sdkref/latest/guide/common-runtime.html) 之上，是替代的 S3 非同步用戶端。它會自動使用 Amazon S3 的[分段上傳 API](https://docs.aws.amazon.com/AmazonS3/latest/userguide/mpuoverview.html) 和[位元組範圍擷取](https://docs.aws.amazon.com/AmazonS3/latest/userguide/optimizing-performance-guidelines.html#optimizing-performance-guidelines-get-range)，以增強的效能和可靠性在 Amazon S3) 之間來回傳輸物件。

 AWS CRT 型 S3 用戶端可在發生網路故障時改善傳輸可靠性。透過重試檔案傳輸的個別失敗部分，而不從頭重新開始傳輸，可改善可靠性。

此外，以 AWS CRT 為基礎的 S3 用戶端提供增強型連線集區和網域名稱系統 (DNS) 負載平衡，也可改善輸送量。

您可以使用 AWS CRT 型 S3 用戶端取代 SDK 的標準 S3 非同步用戶端，並立即利用其改善的輸送量。

**重要**  
 AWS CRT 型 S3 用戶端目前不支援用戶端層級或請求層級的 [SDK 指標集合](metrics.md)。

**AWS 開發套件中的 CRT 型元件**

本主題中所述的 AWS CRT 型* S3* 用戶端和 AWS CRT 型 *HTTP* 用戶端是 SDK 中的不同元件。

**AWS CRT 型 S3 用戶端**是 [S3AsyncClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClient.html) 界面的實作，用於使用 Amazon S3 服務。這是以 Java 為基礎的`S3AsyncClient`介面實作的替代方案，並提供多種優點。

[AWS CRT 型 HTTP 用戶端](http-configuration-crt.md)是 [SdkAsyncHttpClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/async/SdkAsyncHttpClient.html) 界面的實作，用於一般 HTTP 通訊。這是`SdkAsyncHttpClient`介面 Netty 實作的替代方案，並提供數種優點。

雖然這兩個元件都使用[AWS 通用執行期](https://docs.aws.amazon.com/sdkref/latest/guide/common-runtime.html)的程式庫，但以 AWS CRT 為基礎的 S3 用戶端會使用 [aws-c-s3 程式庫](https://github.com/awslabs/aws-c-s3)，並支援 [S3 分段上傳 API](https://docs.aws.amazon.com/AmazonS3/latest/userguide/mpuoverview.html) 功能。由於 AWS CRT 型 HTTP 用戶端適用於一般用途，因此不支援 S3 分段上傳 API 功能。

## 新增相依性以使用 AWS CRT 型 S3 用戶端
<a name="crt-based-s3-client-depend"></a>

若要使用 AWS CRT 型 S3 用戶端，請將下列兩個相依性新增至 Maven 專案檔案。此範例顯示要使用的最低版本。搜尋 Maven 中央儲存庫以取得最新版本的 [s3](https://central.sonatype.com/artifact/software.amazon.awssdk/s3) 和 [aws-crt](https://central.sonatype.com/artifact/software.amazon.awssdk.crt/aws-crt) 成品。

```
<dependency>
  <groupId>software.amazon.awssdk</groupId>
  <artifactId>s3</artifactId>
  <version>2.27.21</version>
</dependency>
<dependency>
  <groupId>software.amazon.awssdk.crt</groupId>
  <artifactId>aws-crt</artifactId>
  <version>0.30.11</version>
</dependency>
```

## 建立 AWS CRT 型 S3 用戶端的執行個體
<a name="crt-based-s3-client-create"></a>

 使用預設設定建立 AWS CRT 型 S3 用戶端的執行個體，如下列程式碼片段所示。

```
S3AsyncClient s3AsyncClient = S3AsyncClient.crtCreate();
```

若要設定用戶端，請使用 AWS CRT 用戶端建置器。您可以透過變更建置器方法，從標準 S3 非同步用戶端切換至 AWS CRT 型用戶端。

```
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3AsyncClient;


S3AsyncClient s3AsyncClient = 
        S3AsyncClient.crtBuilder()
                     .credentialsProvider(DefaultCredentialsProvider.create())
                     .region(Region.US_WEST_2)
                     .targetThroughputInGbps(20.0)
                     .minimumPartSizeInBytes(8 * 1025 * 1024L)
                     .build();
```

**注意**  
 AWS CRT 用戶端建置器目前可能不支援標準建置器中的某些設定。呼叫 以取得標準建置器`S3AsyncClient#builder()`。

## 使用 AWS CRT 型 S3 用戶端
<a name="crt-based-s3-client-use"></a>

使用 AWS CRT 型 S3 用戶端呼叫 Amazon S3 API 操作。下列範例示範 [PutObject](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClient.html#putObject(java.util.function.Consumer,software.amazon.awssdk.core.async.AsyncRequestBody)) 和 [GetObject](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClient.html#getObject(java.util.function.Consumer,software.amazon.awssdk.core.async.AsyncResponseTransformer)) 操作可透過 取得 適用於 Java 的 AWS SDK。

```
import software.amazon.awssdk.core.async.AsyncRequestBody;
import software.amazon.awssdk.core.async.AsyncResponseTransformer;
import software.amazon.awssdk.services.s3.S3AsyncClient;
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
import software.amazon.awssdk.services.s3.model.PutObjectResponse;


S3AsyncClient s3Client = S3AsyncClient.crtCreate();

// Upload a local file to Amazon S3.
PutObjectResponse putObjectResponse = 
      s3Client.putObject(req -> req.bucket(<BUCKET_NAME>)
                                   .key(<KEY_NAME>),
                        AsyncRequestBody.fromFile(Paths.get(<FILE_NAME>)))
              .join();

// Download an object from Amazon S3 to a local file.
GetObjectResponse getObjectResponse = 
      s3Client.getObject(req -> req.bucket(<BUCKET_NAME>)
                                   .key(<KEY_NAME>),
                        AsyncResponseTransformer.toFile(Paths.get(<FILE_NAME>)))
              .join();
```

## 上傳大小不明的串流
<a name="crt-stream-unknown-size"></a>

 AWS AWS CRT 型 S3 用戶端的一個重要優點是能夠有效地處理未知大小的輸入串流。當您需要從無法事先決定總大小的來源上傳資料時，這特別有用。

```
public PutObjectResponse crtClient_stream_unknown_size(String bucketName, String key, InputStream inputStream) {

    S3AsyncClient s3AsyncClient = S3AsyncClient.crtCreate();
    ExecutorService executor = Executors.newSingleThreadExecutor();
    AsyncRequestBody body = AsyncRequestBody.fromInputStream(inputStream, null, executor);  // 'null' indicates that the
                                                                                            // content length is unknown.
    CompletableFuture<PutObjectResponse> responseFuture =
            s3AsyncClient.putObject(r -> r.bucket(bucketName).key(key), body)
                    .exceptionally(e -> {
                        if (e != null){
                            logger.error(e.getMessage(), e);
                        }
                        return null;
                    });

    PutObjectResponse response = responseFuture.join(); // Wait for the response.
    executor.shutdown();
    return response;
}
```

此功能有助於避免傳統上傳的常見問題，其中不正確的內容長度規格可能會導致截斷物件或上傳失敗。

## 組態限制
<a name="crt-based-s3-client-limitations"></a>

 AWS CRT 型 S3 用戶端和 Java 型 S3 非同步用戶端[提供可比較的功能](examples-s3.md#s3-clients)，而 AWS CRT 型 S3 用戶端提供效能邊緣。不過，以 AWS CRT 為基礎的 S3 用戶端缺少以 Java 為基礎的 S3 非同步用戶端所擁有的組態設定。這些設定包括：
+ *用戶端層級組態：*API 呼叫嘗試逾時、壓縮執行攔截器、指標發佈者、自訂執行屬性、自訂進階選項、自訂排程執行器服務、自訂標頭
+ *請求層級組態：*自訂簽署者、API 呼叫嘗試逾時

如需組態差異的完整清單，請參閱 API 參考。


| Java 型 S3 非同步用戶端 | AWS CRT 型 S3 用戶端 | 
| --- | --- | 
| 用戶端層級組態[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/sdk-for-java/latest/developer-guide/crt-based-s3-client.html)請求層級組態[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/sdk-for-java/latest/developer-guide/crt-based-s3-client.html) | 用戶端層級組態[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/sdk-for-java/latest/developer-guide/crt-based-s3-client.html)請求層級組態[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/sdk-for-java/latest/developer-guide/crt-based-s3-client.html) | 

# 將 Java 型 S3 非同步用戶端設定為使用平行傳輸
<a name="s3-async-client-multipart"></a>

自 2.27.5 版起，標準 Java 型 S3 非同步用戶端支援自動平行傳輸 （分段上傳和下載）。您可以在建立 Java 型 S3 非同步用戶端時設定平行傳輸的支援。

本節說明如何啟用平行傳輸，以及如何自訂組態。

## 建立 執行個體 `S3AsyncClient`
<a name="s3-async-client-multipart-create"></a>

當您在不呼叫[建置器](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClientBuilder.html)上的任何`multipart*`方法的情況下建立`S3AsyncClient`執行個體時，不會啟用平行傳輸。下列每個陳述式都會建立 Java 型 S3 非同步用戶端，而不支援分段上傳和下載。

### 在沒有分段支援*的情況下*建立
<a name="s3-async-client-mp-off"></a>

**Example**  

```
import software.amazon.awssdk.auth.credentials.ProcessCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3AsyncClient;


S3AsyncClient s3Client = S3AsyncClient.create();

S3AsyncClient s3Client2 = S3AsyncClient.builder().build();

S3AsyncClient s3Client3 = S3AsyncClient.builder()
        .credentialsProvider(ProcessCredentialsProvider.builder().build())
        .region(Region.EU_NORTH_1)
        .build();
```

### *使用*分段支援建立
<a name="s3-async-client-mp-on"></a>

若要使用預設設定啟用平行傳輸，請呼叫建置器`multipartEnabled`上的 並傳入，`true`如下列範例所示。

**Example**  

```
S3AsyncClient s3AsyncClient2 = S3AsyncClient.builder()
        .multipartEnabled(true)
        .build();
```

`thresholdInBytes` 和 `minimumPartSizeInBytes`設定的預設值為 8 MiB。

如果您自訂分段設定，會自動啟用平行傳輸，如下所示。

**Example**  

```
import software.amazon.awssdk.services.s3.S3AsyncClient;
import static software.amazon.awssdk.transfer.s3.SizeConstant.MB;


S3AsyncClient s3AsyncClient2 = S3AsyncClient.builder()
        .multipartConfiguration(b -> b
                .thresholdInBytes(16 * MB)
                .minimumPartSizeInBytes(10 * MB))
        .build();
```

## 上傳大小不明的串流
<a name="java-async-client-stream-unknown-size"></a>

啟用分段的 Java 型 S3 非同步用戶端可以有效地處理輸入串流，其中總大小事先未知：

```
public PutObjectResponse asyncClient_multipart_stream_unknown_size(String bucketName, String key, InputStream inputStream) {

    S3AsyncClient s3AsyncClient = S3AsyncClient.builder().multipartEnabled(true).build();
    ExecutorService executor = Executors.newSingleThreadExecutor();
    AsyncRequestBody body = AsyncRequestBody.fromInputStream(inputStream, null, executor); // 'null' indicates that the
                                                                                           // content length is unknown.
    CompletableFuture<PutObjectResponse> responseFuture =
            s3AsyncClient.putObject(r -> r.bucket(bucketName).key(key), body)
                    .exceptionally(e -> {
                        if (e != null) {
                            logger.error(e.getMessage(), e);
                        }
                        return null;
                    });

    PutObjectResponse response = responseFuture.join(); // Wait for the response.
    executor.shutdown();
    return response;
}
```

此方法可防止手動指定不正確的內容長度時可能發生的問題，例如截斷物件或上傳失敗。

# 使用 Amazon S3 Transfer Manager 傳輸檔案和目錄
<a name="transfer-manager"></a>

Amazon S3 Transfer Manager 是 的開放原始碼、高階檔案傳輸公用程式 AWS SDK for Java 2.x。使用它在 Amazon Simple Storage Service (Amazon S3) 之間傳輸檔案和目錄。

在啟用分段的 [AWS CRT 型 S3 用戶端](crt-based-s3-client.md)或標準 Java 型 S3 非同步用戶端上建置時，S3 Transfer Manager 可以利用效能改善，例如[分段上傳 API](https://docs.aws.amazon.com/AmazonS3/latest/userguide/mpuoverview.html) [和位元組範圍擷取](https://docs.aws.amazon.com/AmazonS3/latest/userguide/optimizing-performance-guidelines.html#optimizing-performance-guidelines-get-range)。 [ S3 ](s3-async-client-multipart.md) 

使用 S3 Transfer Manager，您也可以即時監控傳輸進度，並暫停傳輸以供稍後執行。

## 開始使用
<a name="transfer-manager-prerequisites"></a>

### 將相依性新增至您的建置檔案
<a name="transfer-manager-add-dependency"></a>

若要使用具有增強分段效能的 S3 Transfer Manager，請設定具有必要相依性的建置檔案。

------
#### [ Use the AWS CRT-based S3 client ]

```
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>bom</artifactId>
            <version>2.27.211</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
    <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>s3-transfer-manager</artifactId>
    </dependency>
    <dependency>
        <groupId>software.amazon.awssdk.crt</groupId>
        <artifactId>aws-crt</artifactId>
        <version>0.29.1432</version>
    </dependency>
</dependencies>
```

1 [最新版本](https://central.sonatype.com/artifact/software.amazon.awssdk/bom)。 2[最新版本](https://central.sonatype.com/artifact/software.amazon.awssdk.crt/aws-crt)。

------
#### [ Use the Java-based S3 async client ]

```
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>bom</artifactId>
            <version>2.27.211</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
    <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>s3-transfer-manager</artifactId>
    </dependency>
</dependencies>
```

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

------

### 建立 S3 Transfer Manager 的執行個體
<a name="transfer-manager-create"></a>

若要啟用平行傳輸，您必須傳入 AWS CRT 型 S3 用戶端或啟用分段的 Java 型 S3 非同步用戶端。下列範例示範如何使用自訂設定來設定 S3 Transfer Manager。

------
#### [ Use the AWS CRT-based S3 client ]

```
        S3AsyncClient s3AsyncClient = S3AsyncClient.crtBuilder()
                .credentialsProvider(DefaultCredentialsProvider.create())
                .region(Region.US_EAST_1)
                .targetThroughputInGbps(20.0)
                .minimumPartSizeInBytes(8 * MB)
                .build();

        S3TransferManager transferManager = S3TransferManager.builder()
                .s3Client(s3AsyncClient)
                .build();
```

------
#### [ Use the Java-based S3 async client ]

如果建置檔案中未包含`aws-crt`相依性，S3 Transfer Manager 會建置在適用於 Java 2.x 的 SDK 中使用的標準 Java 型 S3 非同步用戶端之上。

**S3 用戶端的自訂組態 - 需要啟用分段**

```
        S3AsyncClient s3AsyncClient = S3AsyncClient.builder()
                .multipartEnabled(true)
                .credentialsProvider(DefaultCredentialsProvider.create())
                .region(Region.US_EAST_1)
                .build();

        S3TransferManager transferManager = S3TransferManager.builder()
                .s3Client(s3AsyncClient)
                .build();
```

**沒有 S3 用戶端的組態 - 自動啟用分段支援**

```
S3TransferManager transferManager = S3TransferManager.create();
```

------

## 將檔案上傳至 S3 儲存貯體
<a name="transfer-manager-upload"></a>

下列範例顯示檔案上傳範例，以及選用的 [LoggingTransferListener](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/progress/LoggingTransferListener.html)，這會記錄上傳進度。

若要使用 Amazon S3 S3，請將[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/UploadFileRequest.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/UploadFileRequest.html)物件傳遞至 `S3TransferManager`的 [uploadFile](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/S3TransferManager.html#uploadFile(software.amazon.awssdk.transfer.s3.model.UploadFileRequest)) 方法。

從 `uploadFile`方法傳回的 [FileUpload](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/FileUpload.html) 物件代表上傳程序。請求完成後，[CompletedFileUpload](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/CompletedFileUpload.html) 物件會包含上傳的相關資訊。

```
    public void trackUploadFile(S3TransferManager transferManager, String bucketName,
                             String key, URI filePathURI) {
        UploadFileRequest uploadFileRequest = UploadFileRequest.builder()
                .putObjectRequest(b -> b.bucket(bucketName).key(key))
                .addTransferListener(LoggingTransferListener.create())  // Add listener.
                .source(Paths.get(filePathURI))
                .build();

        FileUpload fileUpload = transferManager.uploadFile(uploadFileRequest);

        fileUpload.completionFuture().join();
        /*
            The SDK provides a LoggingTransferListener implementation of the TransferListener interface.
            You can also implement the interface to provide your own logic.

            Configure log4J2 with settings such as the following.
                <Configuration status="WARN">
                    <Appenders>
                        <Console name="AlignedConsoleAppender" target="SYSTEM_OUT">
                            <PatternLayout pattern="%m%n"/>
                        </Console>
                    </Appenders>

                    <Loggers>
                        <logger name="software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener" level="INFO" additivity="false">
                            <AppenderRef ref="AlignedConsoleAppender"/>
                        </logger>
                    </Loggers>
                </Configuration>

            Log4J2 logs the progress. The following is example output for a 21.3 MB file upload.
                Transfer initiated...
                |                    | 0.0%
                |====                | 21.1%
                |============        | 60.5%
                |====================| 100.0%
                Transfer complete!
        */
    }
```

### 匯入
<a name="transfer-manager-upload-imports"></a>

```
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.transfer.s3.S3TransferManager;
import software.amazon.awssdk.transfer.s3.model.CompletedFileUpload;
import software.amazon.awssdk.transfer.s3.model.FileUpload;
import software.amazon.awssdk.transfer.s3.model.UploadFileRequest;
import software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Paths;
import java.util.UUID;
```

## 從 S3 儲存貯體下載檔案
<a name="transfer-manager-download"></a>

下列範例顯示下載範例，以及選用的 [LoggingTransferListener](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/progress/LoggingTransferListener.html)，這會記錄下載進度。

若要使用 S3 Transfer Manager 從 S3 儲存貯體下載物件，請建置 [DownloadFileRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/DownloadFileRequest.html) 物件並將其傳遞至 [downloadFile](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/S3TransferManager.html#downloadFile(software.amazon.awssdk.transfer.s3.model.DownloadFileRequest)) 方法。

的 `downloadFile`方法傳回`S3TransferManager`的 [FileDownload](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/FileDownload.html) 物件代表檔案傳輸。下載完成後，[CompletedFileDownload](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/CompletedFileDownload.html) 會包含下載相關資訊的存取權。

```
    public void trackDownloadFile(S3TransferManager transferManager, String bucketName,
                             String key, String downloadedFileWithPath) {
        DownloadFileRequest downloadFileRequest = DownloadFileRequest.builder()
                .getObjectRequest(b -> b.bucket(bucketName).key(key))
                .addTransferListener(LoggingTransferListener.create())  // Add listener.
                .destination(Paths.get(downloadedFileWithPath))
                .build();

        FileDownload downloadFile = transferManager.downloadFile(downloadFileRequest);

        CompletedFileDownload downloadResult = downloadFile.completionFuture().join();
        /*
            The SDK provides a LoggingTransferListener implementation of the TransferListener interface.
            You can also implement the interface to provide your own logic.

            Configure log4J2 with settings such as the following.
                <Configuration status="WARN">
                    <Appenders>
                        <Console name="AlignedConsoleAppender" target="SYSTEM_OUT">
                            <PatternLayout pattern="%m%n"/>
                        </Console>
                    </Appenders>

                    <Loggers>
                        <logger name="software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener" level="INFO" additivity="false">
                            <AppenderRef ref="AlignedConsoleAppender"/>
                        </logger>
                    </Loggers>
                </Configuration>

            Log4J2 logs the progress. The following is example output for a 21.3 MB file download.
                Transfer initiated...
                |=======             | 39.4%
                |===============     | 78.8%
                |====================| 100.0%
                Transfer complete!
        */
    }
```

### 匯入
<a name="transfer-manager-download-import"></a>

```
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.core.exception.SdkException;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.transfer.s3.S3TransferManager;
import software.amazon.awssdk.transfer.s3.model.CompletedFileDownload;
import software.amazon.awssdk.transfer.s3.model.DownloadFileRequest;
import software.amazon.awssdk.transfer.s3.model.FileDownload;
import software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener;

import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;
```

## 將 Amazon S3 物件複製到另一個儲存貯體
<a name="transfer-manager-copy"></a>

下列範例示範如何使用 S3 Transfer Manager 複製物件。

若要開始將物件從 S3 儲存貯體複製到另一個儲存貯體，請建立基本的 [CopyObjectRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/model/CopyObjectRequest.html) 執行個體。

接著，在 [CopyRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/CopyRequest.html) `CopyObjectRequest`中包裝基本 ，供 S3 Transfer Manager 使用。

`S3TransferManager`的 `copy`方法傳回的`Copy`物件代表複製程序。複製程序完成後，[CompletedCopy](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/CompletedCopy.html) 物件會包含回應的詳細資訊。

```
    public String copyObject(S3TransferManager transferManager, String bucketName,
            String key, String destinationBucket, String destinationKey) {
        CopyObjectRequest copyObjectRequest = CopyObjectRequest.builder()
                .sourceBucket(bucketName)
                .sourceKey(key)
                .destinationBucket(destinationBucket)
                .destinationKey(destinationKey)
                .build();

        CopyRequest copyRequest = CopyRequest.builder()
                .copyObjectRequest(copyObjectRequest)
                .build();

        Copy copy = transferManager.copy(copyRequest);

        CompletedCopy completedCopy = copy.completionFuture().join();
        return completedCopy.response().copyObjectResult().eTag();
    }
```

**注意**  
若要使用 S3 Transfer Manager 執行跨區域複製，請在 AWS CRT 型 S3 用戶端建置器`crossRegionAccessEnabled`上啟用 ，如下列程式碼片段所示。  

```
S3AsyncClient s3AsyncClient = S3AsyncClient.crtBuilder()
                .crossRegionAccessEnabled(true)
                .build();

S3TransferManager transferManager = S3TransferManager.builder()
                .s3Client(s3AsyncClient)
                .build();
```

### 匯入
<a name="transfer-manager-copy-import"></a>

```
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.model.CopyObjectRequest;
import software.amazon.awssdk.transfer.s3.S3TransferManager;
import software.amazon.awssdk.transfer.s3.model.CompletedCopy;
import software.amazon.awssdk.transfer.s3.model.Copy;
import software.amazon.awssdk.transfer.s3.model.CopyRequest;

import java.util.UUID;
```

## 將本機目錄上傳至 S3 儲存貯體
<a name="transfer-manager-upload_directory"></a>

下列範例示範如何將本機目錄上傳至 S3。

首先呼叫`S3TransferManager`執行個體的 [uploadDirectory](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/S3TransferManager.html#uploadDirectory(software.amazon.awssdk.transfer.s3.model.UploadDirectoryRequest)) 方法，傳入 [UploadDirectoryRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/UploadDirectoryRequest.html)。

[DirectoryUpload](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/DirectoryUpload.html) 物件代表上傳程序，該程序會在請求完成時產生 [CompletedDirectoryUpload](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/CompletedDirectoryUpload.html)。`CompleteDirectoryUpload` 物件包含傳輸結果的相關資訊，包括哪些檔案無法傳輸。

```
    public Integer uploadDirectory(S3TransferManager transferManager,
            URI sourceDirectory, String bucketName) {
        DirectoryUpload directoryUpload = transferManager.uploadDirectory(UploadDirectoryRequest.builder()
                .source(Paths.get(sourceDirectory))
                .bucket(bucketName)
                .build());

        CompletedDirectoryUpload completedDirectoryUpload = directoryUpload.completionFuture().join();
        completedDirectoryUpload.failedTransfers()
                .forEach(fail -> logger.warn("Object [{}] failed to transfer", fail.toString()));
        return completedDirectoryUpload.failedTransfers().size();
    }
```

### 匯入
<a name="transfer-manager-upload_directory-import"></a>

```
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.services.s3.model.ObjectIdentifier;
import software.amazon.awssdk.transfer.s3.S3TransferManager;
import software.amazon.awssdk.transfer.s3.model.CompletedDirectoryUpload;
import software.amazon.awssdk.transfer.s3.model.DirectoryUpload;
import software.amazon.awssdk.transfer.s3.model.UploadDirectoryRequest;

import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Paths;
import java.util.UUID;
```

## 將 S3 儲存貯體物件下載至本機目錄
<a name="transfer-manager-download_directory"></a>

您可以將 S3 儲存貯體中的物件下載至本機目錄，如下列範例所示。

若要將 S3 儲存貯體中的物件下載至本機目錄，請先呼叫 Transfer Manager 的 [downloadDirectory](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/S3TransferManager.html#downloadDirectory(software.amazon.awssdk.transfer.s3.model.DownloadDirectoryRequest)) 方法，並傳入 [DownloadDirectoryRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/DownloadDirectoryRequest.html)。

[DirectoryDownload](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/DirectoryDownload.html) 物件代表下載程序，該程序會在請求完成時產生 [CompletedDirectoryDownload](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/CompletedDirectoryDownload.html)。`CompleteDirectoryDownload` 物件包含傳輸結果的相關資訊，包括哪些檔案無法傳輸。

```
    public Integer downloadObjectsToDirectory(S3TransferManager transferManager,
            URI destinationPathURI, String bucketName) {
        DirectoryDownload directoryDownload = transferManager.downloadDirectory(DownloadDirectoryRequest.builder()
                .destination(Paths.get(destinationPathURI))
                .bucket(bucketName)
                .build());
        CompletedDirectoryDownload completedDirectoryDownload = directoryDownload.completionFuture().join();

        completedDirectoryDownload.failedTransfers()
                .forEach(fail -> logger.warn("Object [{}] failed to transfer", fail.toString()));
        return completedDirectoryDownload.failedTransfers().size();
    }
```

### 匯入
<a name="transfer-manager-download_directory-import"></a>

```
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.model.ObjectIdentifier;
import software.amazon.awssdk.transfer.s3.S3TransferManager;
import software.amazon.awssdk.transfer.s3.model.CompletedDirectoryDownload;
import software.amazon.awssdk.transfer.s3.model.DirectoryDownload;
import software.amazon.awssdk.transfer.s3.model.DownloadDirectoryRequest;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
```

## 請參閱完整範例
<a name="transfer-manager-example-location"></a>

[GitHub 包含此頁面上所有範例的完整](https://github.com/awsdocs/aws-doc-sdk-examples/tree/d73001daea05266eaa9e074ccb71b9383832369a/javav2/example_code/s3/src/main/java/com/example/s3/transfermanager)程式碼。

# 使用 S3 事件通知
<a name="examples-s3-event-notifications"></a>

為了協助您監控儲存貯體中的活動，Amazon S3 可以在發生特定事件時傳送通知。Amazon S3 使用者指南提供[儲存貯體可傳送之通知](https://docs.aws.amazon.com/AmazonS3/latest/userguide/EventNotifications.html#notification-how-to-overview)的相關資訊。

您可以使用適用於 Java 的 SDK 設定儲存貯體，將事件傳送至四個可能的目的地：
+ Amazon Simple Notification Service 主題
+ Amazon Simple Queue Service 佇列
+ AWS Lambda 函數
+ Amazon EventBridge

當您設定儲存貯體將事件傳送至 EventBridge 時，您可以設定 EventBridge 規則，將相同的事件散發到多個目的地。當您將儲存貯體設定為直接傳送至前三個目的地之一時，每個事件只能指定一個目的地類型。

在下一節中，您將了解如何使用適用於 Java 的 SDK 設定儲存貯體，以兩種方式傳送 S3 事件通知：直接傳送至 Amazon SQS 佇列和 EventBridge。

最後一節說明如何使用 S3 事件通知 API，以物件導向的方式使用通知。

## 設定儲存貯體以直接傳送至目的地
<a name="s3-event-conf-bucket-direct"></a>

下列範例會設定儲存貯體，以在*物件建立*事件或*物件標記*事件發生時傳送通知。

```
static void processS3Events(String bucketName, String queueArn) {
    // Configure the bucket to send Object Created and Object Tagging notifications to an existing SQS queue.
    s3Client.putBucketNotificationConfiguration(b -> b
            .notificationConfiguration(ncb -> ncb
                    .queueConfigurations(qcb -> qcb
                            .events(Event.S3_OBJECT_CREATED, Event.S3_OBJECT_TAGGING)
                            .queueArn(queueArn)))
                    .bucket(bucketName)
    );
}
```

上面顯示的程式碼會設定一個佇列來接收兩種類型的事件。方便使用， `queueConfigurations`方法可讓您視需要設定多個佇列目的地。此外，您可以在 `notificationConfiguration`方法中設定其他目的地，例如一或多個 Amazon SNS 主題或一或多個 Lambda 函數。下列程式碼片段顯示具有兩個佇列和三種目的地類型的範例。

```
s3Client.putBucketNotificationConfiguration(b -> b
                .notificationConfiguration(ncb -> ncb
                        .queueConfigurations(qcb -> qcb
                                .events(Event.S3_OBJECT_CREATED, Event.S3_OBJECT_TAGGING)
                                .queueArn(queueArn), 
                                qcb2 -> qcb2.<...>)
                        .topicConfigurations(tcb -> tcb.<...>)
                        .lambdaFunctionConfigurations(lfcb -> lfcb.<...>))
                        .bucket(bucketName)
        );
```

程式碼範例 GitHub 儲存庫包含將 S3 事件通知直接傳送到佇列[的完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/main/javav2/example_code/s3/src/main/java/com/example/s3/ProcessS3EventNotification.java)。

## 設定要傳送至 EventBridge 的儲存貯體
<a name="s3-event-conf-bucket-eventbridge"></a>

下列範例會將儲存貯體設定為傳送通知至 EventBridge。

```
public static String setBucketNotificationToEventBridge(String bucketName) {
    // Enable bucket to emit S3 Event notifications to EventBridge.
    s3Client.putBucketNotificationConfiguration(b -> b
            .bucket(bucketName)
            .notificationConfiguration(b1 -> b1
                    .eventBridgeConfiguration(SdkBuilder::build))
    .build());
```

當您設定儲存貯體將事件傳送至 EventBridge 時，您只需指出 EventBridge 目的地，而不是 EventBridge 將發送的事件類型或最終目的地。您可以使用 Java 開發套件的 EventBridge 用戶端來設定最終目標和事件類型。

下列程式碼說明如何設定 EventBridge 將*物件建立*的事件散發至主題和佇列。

```
   public static String configureEventBridge(String topicArn, String queueArn) {
        try {
            // Create an EventBridge rule to route Object Created notifications.
            PutRuleRequest putRuleRequest = PutRuleRequest.builder()
                    .name(RULE_NAME)
                    .eventPattern("""
                            {
                              "source": ["aws.s3"],
                              "detail-type": ["Object Created"],
                              "detail": {
                                "bucket": {
                                  "name": ["%s"]
                                }
                              }
                            }
                            """.formatted(bucketName))
                    .build();

            // Add the rule to the default event bus.
            PutRuleResponse putRuleResponse = eventBridgeClient.putRule(putRuleRequest)
                    .whenComplete((r, t) -> {
                        if (t != null) {
                            logger.error("Error creating event bus rule: " + t.getMessage(), t);
                            throw new RuntimeException(t.getCause().getMessage(), t);
                        }
                        logger.info("Event bus rule creation request sent successfully. ARN is: {}", r.ruleArn());
                    }).join();

            // Add the existing SNS topic and SQS queue as targets to the rule.
            eventBridgeClient.putTargets(b -> b
                    .eventBusName("default")
                    .rule(RULE_NAME)
                    .targets(List.of (
                            Target.builder()
                                    .arn(queueArn)
                                    .id("Queue")
                                    .build(),
                            Target.builder()
                                    .arn(topicArn)
                                    .id("Topic")
                                    .build())
                            )
                    ).join();
            return putRuleResponse.ruleArn();
        } catch (S3Exception e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
        return null;
    }
```

若要在 Java 程式碼中使用 EventBridge，請將`eventbridge`成品的相依性新增至 Maven `pom.xml` 檔案。

```
<dependency>
    <groupId>software.amazon.awssdk</groupId>
    <artifactId>eventbridge</artifactId>
</dependency>
```

程式碼範例 GitHub 儲存庫包含傳送 S3 事件通知至 EventBridge，然後傳送至主題和佇列[的完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/main/javav2/example_code/s3/src/main/java/com/example/s3/PutBucketS3EventNotificationEventBridge.java)。

## 使用 S3 事件通知 API 來處理事件
<a name="s3-event-notification-read"></a>

目的地收到 S3 通知事件後，您可以使用 S3 事件通知 API，以物件導向的方式處理它們。您可以使用 S3 事件通知 API 來處理直接分派至目標的事件通知 （如[第一個範例](#s3-event-conf-bucket-direct)所示），但不適用於透過 EventBridge 路由的通知。儲存貯體傳送至 EventBridge 的 S3 事件通知包含 S3 事件通知 API 目前未處理[的不同結構](https://docs.aws.amazon.com/AmazonS3/latest/userguide/ev-events.html#ev-events-list)。

### 新增相依性
<a name="s3-event-notifications-dep"></a>

S3 事件通知 API 已搭配適用於 Java 的 SDK 2.x 2.25.11 版發行。

若要使用 S3 事件通知 API，請將必要的相依性元素新增至 Maven`pom.xml`，如下列程式碼片段所示。

```
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>bom</artifactId>
            <version>2.X.X1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
    <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>s3-event-notifications</artifactId>
    </dependency>
</dependencies>
```

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

### 使用 `S3EventNotification`類別
<a name="s3-event-notifications-use"></a>

#### 從 JSON 字串建立`S3EventNotification`執行個體
<a name="s3-event-notifications-use-from-json"></a>

若要將 JSON 字串轉換為`S3EventNotification`物件，請使用 `S3EventNotification`類別的靜態方法，如下列範例所示。

```
import software.amazon.awssdk.eventnotifications.s3.model.S3EventNotification
import software.amazon.awssdk.eventnotifications.s3.model.S3EventNotificationRecord
import software.amazon.awssdk.services.sqs.model.Message; 

public class S3EventNotificationExample {
    ...
    
    void receiveMessage(Message message) {
       // Message received from SQSClient.
       String sqsEventBody = message.body();
       S3EventNotification s3EventNotification = S3EventNotification.fromJson(sqsEventBody);
    
       // Use getRecords() to access all the records in the notification.                                                                                                       
       List<S3EventNotificationRecord> records = s3EventNotification.getRecords();   
    
        S3EventNotificationRecord record = records.stream().findFirst();
        // Use getters on the record to access individual attributes.
        String awsRegion = record.getAwsRegion();
        String eventName = record.getEventName();
        String eventSource = record.getEventSource();                                                                                                   
    }
}
```

在此範例中， `fromJson`方法會將 JSON 字串轉換為 `S3EventNotification` 物件。JSON 字串中缺少的欄位會導致對應 Java 物件欄位中`null`的值，且 JSON 中的任何額外欄位都會遭到忽略。

您可以在 APIs 參考中找到事件通知記錄的其他 API`[S3EventNotificationRecord](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/eventnotifications/s3/model/S3EventNotificationRecord.html)`。

#### 將`S3EventNotification`執行個體轉換為 JSON 字串
<a name="s3-event-notifications-use-to-json"></a>

使用 `toJson`（或 `toJsonPretty`) 方法將`S3EventNotification`物件轉換為 JSON 字串，如下列範例所示。

```
import software.amazon.awssdk.eventnotifications.s3.model.S3EventNotification

public class S3EventNotificationExample {
    ...

    void toJsonString(S3EventNotification event) {

        String json = event.toJson();
        String jsonPretty = event.toJsonPretty();

        System.out.println("JSON: " + json);
        System.out.println("Pretty JSON: " + jsonPretty);
    }
}
```

如果 `GlacierEventData`、`IntelligentTieringEventData`、 `ReplicationEventData`和 的欄位是 ，`LifecycleEventData`則會從 JSON 中排除`null`。其他`null`欄位將序列化為 `null`。

以下顯示 S3 物件標記事件的 `toJsonPretty`方法輸出範例。

```
{
  "Records" : [ {
    "eventVersion" : "2.3",
    "eventSource" : "aws:s3",
    "awsRegion" : "us-east-1",
    "eventTime" : "2024-07-19T20:09:18.551Z",
    "eventName" : "ObjectTagging:Put",
    "userIdentity" : {
      "principalId" : "AWS:XXXXXXXXXXX"
    },
    "requestParameters" : {
      "sourceIPAddress" : "XXX.XX.XX.XX"
    },
    "responseElements" : {
      "x-amz-request-id" : "XXXXXXXXXXXX",
      "x-amz-id-2" : "XXXXXXXXXXXXX"
    },
    "s3" : {
      "s3SchemaVersion" : "1.0",
      "configurationId" : "XXXXXXXXXXXXX",
      "bucket" : {
        "name" : "amzn-s3-demo-bucket",
        "ownerIdentity" : {
          "principalId" : "XXXXXXXXXXX"
        },
        "arn" : "arn:aws:s3:::XXXXXXXXXX"
      },
      "object" : {
        "key" : "akey",
        "size" : null,
        "eTag" : "XXXXXXXXXX",
        "versionId" : null,
        "sequencer" : null
      }
    }
  } ]
}
```

GitHub 提供[完整的範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/75c3daadf750406156fc87fa30ee499a206b4a36/javav2/example_code/s3/src/main/java/com/example/s3/ProcessS3EventNotification.java#L117)，說明如何使用 API 來處理 Amazon SQS 佇列收到的通知。

## 使用 Java 程式庫處理 Lambda 中的 S3 事件： AWS SDK for Java 2.x 和 `aws-lambda-java-events`
<a name="s3-event-notif-processing-options"></a>

與其使用適用於 Java 的 SDK 2.x 在 Lambda 函數中處理 Amazon S3 事件通知，您可以在 3.x.x 版使用`[aws-lambda-java-events](https://github.com/aws/aws-lambda-java-libs/tree/main/aws-lambda-java-events)`程式庫。 會獨立 AWS 維護`aws-lambda-java-events`程式庫，並且具有自己的相依性要求。`aws-lambda-java-events` 程式庫僅適用於 Lambda 函數中的 S3 事件，而適用於 Java 的 SDK 2.x 僅適用於 Lambda 函數、Amazon SNS 和 Amazon SQS 中的 S3 事件。

這兩種方法都使用類似的 APIs，以物件導向的方式建立 JSON 事件通知承載的模型。下表顯示使用兩種方法之間的顯著差異。


****  

|  | 適用於 Java 的 AWS SDK | aws-lambda-java-events 程式庫 | 
| --- | --- | --- | 
| 套件命名 |  `software.amazon.awssdk.eventnotifications.s3.model.S3EventNotification`  | com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification | 
| RequestHandler 參數 |  撰寫 Lambda 函數的`RequestHandler`實作以接收 JSON 字串： <pre>import com.amazonaws.services.lambda.runtime.Context;<br />import com.amazonaws.services.lambda.runtime.RequestHandler;<br />import software.amazon.awssdk.eventnotifications.s3.model.S3EventNotification;<br /><br />public class Handler implements RequestHandler<String, String> {<br /><br />    @Override<br />        public String handleRequest(String jsonS3Event, Context context) {<br />            S3EventNotification s3Event = S3EventNotification<br />                                             .fromJson(jsonS3Event);<br />            // Work with the s3Event object.        <br />            ...<br />    }<br />}</pre>  | 撰寫 Lambda 函數的RequestHandler實作以接收S3Event物件：<pre>import com.amazonaws.services.lambda.runtime.Context;<br />import com.amazonaws.services.lambda.runtime.RequestHandler;<br />import com.amazonaws.services.lambda.runtime.events.S3Event;<br /><br />public class Handler implements RequestHandler<S3Event, String> {<br /><br />    @Override<br />        public String handleRequest(S3Event s3event, Context context) {<br />            // Work with the s3Event object.        <br />            ...<br />    }<br />}</pre> | 
| Maven 相依性 |  <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 /><dependencies><br />    <dependency><br />        <groupId>software.amazon.awssdk</groupId><br />        <artifactId>s3-event-notifications</artifactId><br />    </dependency><br />    <!-- Add other SDK dependencies that you need. --><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 /><dependencies><br />    <!-- The following two dependencies are for the <br />         aws-lambda-java-events library. --><br />    <dependency><br />        <groupId>com.amazonaws</groupId><br />        <artifactId>aws-lambda-java-core</artifactId><br />        <version>1.2.3</version>     <br />    </dependency><br />    <dependency><br />        <groupId>com.amazonaws</groupId><br />        <artifactId>aws-lambda-java-events</artifactId><br />        <version>3.15.0</version><br />    </dependency><br />    <!-- Add other SDK dependencies that you need. --><br /></dependencies></pre>  | 

# 使用 Amazon Simple Notification Service
<a name="examples-simple-notification-service"></a>

使用 Amazon Simple Notification Service，您可以透過多個通訊管道，輕鬆地將即時通知訊息從應用程式推送至訂閱用戶。本主題說明如何執行 的一些基本函數 Amazon SNS。

## 建立主題
<a name="sns-create-topic"></a>

**主題**是通訊通道的邏輯分組，可定義要傳送訊息的系統，例如發散訊息至 AWS Lambda 和 HTTP Webhook。您可以將訊息傳送到 Amazon SNS，然後它們會分佈到主題中定義的頻道。如此訊息就可用於訂閱者。

若要建立主題，請先建置 [CreateTopicRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sns/model/CreateTopicRequest.html) 物件，並將主題集的名稱用於建置器的 `name()` 方法。然後，使用 [SnsClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sns/SnsClient.html) 的 Amazon SNS `createTopic()`方法將請求物件傳送至 。您可以將此要求的結果擷取為 [CreateTopicResponse](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sns/model/CreateTopicResponse.html) 物件，如下列程式碼片段所示。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sns.SnsClient;
import software.amazon.awssdk.services.sns.model.CreateTopicRequest;
import software.amazon.awssdk.services.sns.model.CreateTopicResponse;
import software.amazon.awssdk.services.sns.model.SnsException;
```

 **Code** 

```
    public static String createSNSTopic(SnsClient snsClient, String topicName ) {

        CreateTopicResponse result = null;
        try {
            CreateTopicRequest request = CreateTopicRequest.builder()
                    .name(topicName)
                    .build();

            result = snsClient.createTopic(request);
            return result.topicArn();
        } catch (SnsException e) {

            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
        return "";
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/da520cb4436f8567a90b6f73f77232fd590a50bf/javav2/example_code/sns/src/main/java/com/example/sns/CreateTopic.java)。

## 列出您的 Amazon SNS 主題
<a name="sns-crelistate-topics"></a>

若要擷取現有 Amazon SNS 主題的清單，請建置 [ListTopicsRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sns/model/ListTopicsRequest.html) 物件。然後， Amazon SNS 使用 的 `listTopics()`方法，將請求物件傳送至 `SnsClient`。您可以將此要求的結果擷取為 [ListTopicsResponse](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sns/model/ListTopicsResponse.html) 物件。

下列程式碼片段會列印請求的 HTTP 狀態碼，以及 Amazon SNS 主題的 Amazon Resource Name (ARNs) 清單。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sns.SnsClient;
import software.amazon.awssdk.services.sns.model.ListTopicsRequest;
import software.amazon.awssdk.services.sns.model.ListTopicsResponse;
import software.amazon.awssdk.services.sns.model.SnsException;
```

 **Code** 

```
    public static void listSNSTopics(SnsClient snsClient) {

        try {
            ListTopicsRequest request = ListTopicsRequest.builder()
                   .build();

            ListTopicsResponse result = snsClient.listTopics(request);
            System.out.println("Status was " + result.sdkHttpResponse().statusCode() + "\n\nTopics\n\n" + result.topics());

        } catch (SnsException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/da520cb4436f8567a90b6f73f77232fd590a50bf/javav2/example_code/sns/src/main/java/com/example/sns/ListTopics.java)。

## 讓端點訂閱主題
<a name="sns-subscribe-endpoint-topic"></a>

建立主題之後，您可以設定哪些通訊頻道將成為該主題的端點。訊息會在 Amazon SNS 收到後分發到這些端點。

若要將通訊頻道設定為主題的端點，請讓該端點訂閱主題。若要開始，請建置 [SubscribeRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sns/model/SubscribeRequest.html) 物件。將通訊管道 （例如 `lambda`或 `email`) 指定為 `protocol()`。將 `endpoint()`設定為相關的輸出位置 （例如， Lambda 函數的 ARN 或電子郵件地址），然後將您要訂閱的主題 ARN 設定為 `topicArn()`。 Amazon SNS 使用 的 `subscribe()`方法，將請求物件傳送至 `SnsClient`。您可以將此要求的結果擷取為 [SubscribeResponse](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sns/model/SubscribeResponse.html) 物件。

下列程式碼片段示範如何讓電子郵件地址訂閱主題。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sns.SnsClient;
import software.amazon.awssdk.services.sns.model.SnsException;
import software.amazon.awssdk.services.sns.model.SubscribeRequest;
import software.amazon.awssdk.services.sns.model.SubscribeResponse;
```

 **Code** 

```
    public static void subEmail(SnsClient snsClient, String topicArn, String email) {

        try {
            SubscribeRequest request = SubscribeRequest.builder()
                .protocol("email")
                .endpoint(email)
                .returnSubscriptionArn(true)
                .topicArn(topicArn)
                .build();

            SubscribeResponse result = snsClient.subscribe(request);
            System.out.println("Subscription ARN: " + result.subscriptionArn() + "\n\n Status is " + result.sdkHttpResponse().statusCode());

        } catch (SnsException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/da520cb4436f8567a90b6f73f77232fd590a50bf/javav2/example_code/sns/src/main/java/com/example/sns/SubscribeEmail.java)。

## 發佈訊息至主題
<a name="sns-publish-message-topic"></a>

擁有主題並為其設定一或多個端點之後，您可以將訊息發佈至該主題。若要開始，請建置 [PublishRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sns/model/PublishRequest.html) 物件。指定要傳送的 `message()`，以及要傳送的主題的 ARN (`topicArn()`)。然後， Amazon SNS 使用 的 `publish()`方法，將請求物件傳送至 `SnsClient`。您可以將此要求的結果擷取為 [PublishResponse](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sns/model/PublishResponse.html) 物件。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sns.SnsClient;
import software.amazon.awssdk.services.sns.model.PublishRequest;
import software.amazon.awssdk.services.sns.model.PublishResponse;
import software.amazon.awssdk.services.sns.model.SnsException;
```

 **Code** 

```
    public static void pubTopic(SnsClient snsClient, String message, String topicArn) {

        try {
            PublishRequest request = PublishRequest.builder()
                .message(message)
                .topicArn(topicArn)
                .build();

            PublishResponse result = snsClient.publish(request);
            System.out.println(result.messageId() + " Message sent. Status is " + result.sdkHttpResponse().statusCode());

         } catch (SnsException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
         }
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/da520cb4436f8567a90b6f73f77232fd590a50bf/javav2/example_code/sns/src/main/java/com/example/sns/PublishTopic.java)。

## 讓端點取消訂閱主題
<a name="sns-unsubscribe-endpoint-topic"></a>

您可以移除設定為主題端點的通訊頻道。這麼做之後，主題本身會繼續存在，並將訊息散佈到針對該主題設定的任何其他端點。

若要從主題端點移除通訊頻道，請從主題取消訂閱該端點。若要開始，請建置 [UnsubscribeRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sns/model/UnsubscribeRequest.html) 物件，並將您要取消訂閱的主題 ARN 設定為 `subscriptionArn()`。然後使用 `SnsClient` 的 `unsubscribe()` 方法，將要求物件傳送至 SNS。您可以將此要求的結果擷取為 [UnsubscribeResponse](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sns/model/UnsubscribeResponse.html) 物件。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sns.SnsClient;
import software.amazon.awssdk.services.sns.model.SnsException;
import software.amazon.awssdk.services.sns.model.UnsubscribeRequest;
import software.amazon.awssdk.services.sns.model.UnsubscribeResponse;
```

 **Code** 

```
    public static void unSub(SnsClient snsClient, String subscriptionArn) {

        try {
            UnsubscribeRequest request = UnsubscribeRequest.builder()
                .subscriptionArn(subscriptionArn)
                .build();

            UnsubscribeResponse result = snsClient.unsubscribe(request);

            System.out.println("\n\nStatus was " + result.sdkHttpResponse().statusCode()
                + "\n\nSubscription was removed for " + request.subscriptionArn());

        } catch (SnsException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/da520cb4436f8567a90b6f73f77232fd590a50bf/javav2/example_code/sns/src/main/java/com/example/sns/Unsubscribe.java)。

## 刪除主題
<a name="sns-delete-topic"></a>

若要刪除 Amazon SNS 主題，請先建置 [DeleteTopicRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sns/model/DeleteTopicRequest.html) 物件，並將主題的 ARN 設定為建置器中的 `topicArn()` 方法。然後使用 的 `deleteTopic()`方法 Amazon SNS ，將請求物件傳送至 `SnsClient`。您可以將此要求的結果擷取為 [DeleteTopicResponse](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sns/model/DeleteTopicResponse.html) 物件，如下列程式碼片段所示。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sns.SnsClient;
import software.amazon.awssdk.services.sns.model.DeleteTopicRequest;
import software.amazon.awssdk.services.sns.model.DeleteTopicResponse;
import software.amazon.awssdk.services.sns.model.SnsException;
```

 **Code** 

```
    public static void deleteSNSTopic(SnsClient snsClient, String topicArn ) {

        try {
            DeleteTopicRequest request = DeleteTopicRequest.builder()
                .topicArn(topicArn)
                .build();

            DeleteTopicResponse result = snsClient.deleteTopic(request);
            System.out.println("\n\nStatus was " + result.sdkHttpResponse().statusCode());

        } catch (SnsException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/da520cb4436f8567a90b6f73f77232fd590a50bf/javav2/example_code/sns/src/main/java/com/example/sns/DeleteTopic.java)。

如需詳細資訊，請參閱《[Amazon Simple Notification Service 開發人員指南](https://docs.aws.amazon.com/sns/latest/dg/)》。

# 使用 Amazon Simple Queue Service
<a name="examples-sqs"></a>

本節提供[Amazon Simple Queue Service](https://docs.aws.amazon.com/sqs/)使用 適用於 Java 的 AWS SDK 2.x 進行程式設計的範例。

下列範例僅包含示範每個技術所需的程式碼。[GitHub 上提供完整程式碼範例](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2)。您可以從那裡下載單一原始檔案或將儲存庫複製到本機，以取得建置和執行的所有範例。

**Topics**
+ [使用自動請求批次處理](sqs-auto-batch.md)
+ [佇列操作](examples-sqs-message-queues.md)
+ [訊息操作](examples-sqs-messages.md)

# 搭配 使用 Amazon SQS 的自動請求批次處理 AWS SDK for Java 2.x
<a name="sqs-auto-batch"></a>

Amazon SQS 的 Automatic Request Batching API 是一個高階程式庫，提供有效的方法來批次處理和緩衝 SQS 操作的請求。透過使用批次處理 API，您可以減少對 SQS 的請求數量，從而提高輸送量並將成本降至最低。

由於批次 API 方法符合`[SqsAsyncClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/SqsAsyncClient.html)`方法 —`sendMessage`、`changeMessageVisibility``deleteMessage`、、 `receiveMessage`— 您可以使用批次 API 做為插入式取代，且變更最少。

本主題概述如何設定和使用適用於 Amazon SQS 的自動請求批次 API。

## 檢查先決條件
<a name="sqs-auto-batch-requirements"></a>

您需要使用適用於 Java 的 SDK *2.x 2.28.0 *版或更新版本，才能存取批次處理 API。Maven `pom.xml`應至少包含下列元素。

```
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>bom</artifactId>
            <version>2.28.231</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
    <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>sqs</artifactId>
    </dependency>
</dependencies>
```

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

## 建立批次管理工具
<a name="sqs-auto-batch-create"></a>

[SqsAsyncBatchManager](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/batchmanager/SqsAsyncBatchManager.html) 介面會實作自動請求批次處理 API。您可以用幾種方式建立管理員的執行個體。

### 使用 的預設組態 `SqsAsyncClient`
<a name="sqs-batch-manager-create-default"></a>

建立批次管理工具最簡單的方法是在現有的 [SqsAsyncClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/SqsAsyncClient.html) 執行個體上呼叫`batchManager`原廠方法。簡單方法會顯示在下列程式碼片段中。

```
SqsAsyncClient asyncClient = SqsAsyncClient.create();
SqsAsyncBatchManager sqsAsyncBatchManager = asyncClient.batchManager();
```

當您使用此方法時，`SqsAsyncBatchManager`執行個體會使用 [覆寫 的組態設定 `SqsAsyncBatchManager`](#sqs-auto-batch-config-settings)區段中資料表中顯示的預設值。此外，`SqsAsyncBatchManager`執行個體會使用其建立來源`ExecutorService``SqsAsyncClient`執行個體的 。

### 使用 自訂組態 `SqsAsyncBatchManager.Builder`
<a name="sqs-batch-manager-create-custom"></a>

如需更進階的使用案例，您可以使用 自訂批次管理工具[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/batchmanager/SqsAsyncBatchManager.Builder.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/batchmanager/SqsAsyncBatchManager.Builder.html)。透過使用此方法來建立`SqsAsyncBatchManager`執行個體，您可以微調批次處理行為。下列程式碼片段示範如何使用建置器來自訂批次處理行為。

```
SqsAsyncBatchManager batchManager = SqsAsyncBatchManager.builder()
    .client(SqsAsyncClient.create())
    .scheduledExecutor(Executors.newScheduledThreadPool(5))
    .overrideConfiguration(b -> b
        .receiveMessageMinWaitDuration(Duration.ofSeconds(10))
        .receiveMessageVisibilityTimeout(Duration.ofSeconds(1))
        .receiveMessageAttributeNames(Collections.singletonList("*"))
        .receiveMessageSystemAttributeNames(Collections.singletonList(MessageSystemAttributeName.ALL)))
    .build();
```

使用此方法時，您可以調整 [覆寫 的組態設定 `SqsAsyncBatchManager`](#sqs-auto-batch-config-settings)區段中資料表中顯示之`BatchOverrideConfiguration`物件的設定。您也可以使用此方法[https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/ScheduledExecutorService.html](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/ScheduledExecutorService.html)為批次管理員提供自訂 。

## 傳送訊息
<a name="sqs-auto-batch-send"></a>

若要使用批次管理工具傳送訊息，請使用 `[SqsAsyncBatchManager\$1sendMessage](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/batchmanager/SqsAsyncBatchManager.html#sendMessage(software.amazon.awssdk.services.sqs.model.SendMessageRequest))`方法。軟體開發套件會緩衝請求，並在達到 `maxBatchSize`或 `sendRequestFrequency`值時以批次方式傳送請求。

下列範例顯示緊接另一個`sendMessage`請求的請求。在此情況下，軟體開發套件會以單一批次傳送兩個訊息。

```
// Sending the first message
CompletableFuture<SendMessageResponse> futureOne = 
    sqsAsyncBatchManager.sendMessage(r -> r.messageBody("One").queueUrl("queue"));

// Sending the second message
CompletableFuture<SendMessageResponse> futureTwo = 
    sqsAsyncBatchManager.sendMessage(r -> r.messageBody("Two").queueUrl("queue"));

// Waiting for both futures to complete and retrieving the responses
SendMessageResponse messageOne = futureOne.join();
SendMessageResponse messageTwo = futureTwo.join();
```

## 變更訊息可見性逾時
<a name="sqs-auto-batch-change-vis"></a>

您可以使用 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/batchmanager/SqsAsyncBatchManager.html#changeMessageVisibility(java.util.function.Consumer)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/batchmanager/SqsAsyncBatchManager.html#changeMessageVisibility(java.util.function.Consumer))方法變更批次中訊息的可見性逾時。軟體開發套件會緩衝請求，並在達到 `maxBatchSize`或 `sendRequestFrequency`值時以批次方式傳送請求。

下列範例顯示如何呼叫 `changeMessageVisibility`方法。

```
CompletableFuture<ChangeMessageVisibilityResponse> futureOne =
    sqsAsyncBatchManager.changeMessageVisibility(r -> 
        r.receiptHandle("receiptHandle")
         .queueUrl("queue"));
ChangeMessageVisibilityResponse response = futureOne.join();
```

## 刪除訊息
<a name="sqs-auto-batch-delete"></a>

您可以使用 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/batchmanager/SqsAsyncBatchManager.html#deleteMessage(java.util.function.Consumer)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/batchmanager/SqsAsyncBatchManager.html#deleteMessage(java.util.function.Consumer))方法刪除批次中的訊息。軟體開發套件會緩衝請求，並在達到 `maxBatchSize`或 `sendRequestFrequency`值時以批次方式傳送請求。

下列範例示範如何呼叫 `deleteMessage`方法。

```
CompletableFuture<DeleteMessageResponse> futureOne = 
    sqsAsyncBatchManager.deleteMessage(r -> 
        r.receiptHandle("receiptHandle")
         .queueUrl("queue"));
DeleteMessageResponse response = futureOne.join();
```

## 接收訊息
<a name="sqs-auto-batch-receive"></a>

### 使用預設設定
<a name="sqs-auto-batch-receive-default-settings"></a>

當您在應用程式中輪詢 `[SqsAsyncBatchManager\$1receiveMessage](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/batchmanager/SqsAsyncBatchManager.html#receiveMessage(java.util.function.Consumer))`方法時，批次管理工具會從其內部緩衝區擷取訊息，開發套件會在背景自動更新。

下列範例顯示如何呼叫 `receiveMessage`方法。

```
CompletableFuture<ReceiveMessageResponse> responseFuture = 
    sqsAsyncBatchManager.receiveMessage(r -> r.queueUrl("queueUrl"));
```

### 使用自訂設定
<a name="sqs-auto-batch-receive-custom-settings"></a>

如果您想要進一步自訂請求，例如設定自訂等待時間並指定要擷取的訊息數量，您可以自訂請求，如下列範例所示。

```
CompletableFuture<ReceiveMessageResponse> response = 
    sqsAsyncBatchManager.receiveMessage(r -> 
        r.queueUrl("queueUrl")
         .waitTimeSeconds(5)
         .visibilityTimeout(20));
```

**注意**  
如果您`receiveMessage`使用包含下列任何參數`[ReceiveMessageRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/model/ReceiveMessageRequest.html)`的 呼叫 ，軟體開發套件會略過批次管理工具並傳送一般非同步`receiveMessage`請求：  
`messageAttributeNames`
`messageSystemAttributeNames`
`messageSystemAttributeNamesWithStrings`
`overrideConfiguration`

## 覆寫 的組態設定 `SqsAsyncBatchManager`
<a name="sqs-auto-batch-config-settings"></a>

您可以在建立`SqsAsyncBatchManager`執行個體時調整下列設定。下列設定清單可在 上取得[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/batchmanager/BatchOverrideConfiguration.Builder.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/batchmanager/BatchOverrideConfiguration.Builder.html)。


| 設定 | Description | 預設值 | 
| --- | --- | --- | 
| maxBatchSize | 每個 SendMessageBatchRequest、 ChangeMessageVisibilityBatchRequest或 每個批次的請求數量上限DeleteMessageBatchRequest。最大值為 10。 | 10 | 
| sendRequestFrequency |  傳送批次之前的時間，除非之前`maxBatchSize`已到達。較高的值可能會減少請求，但會增加延遲。  | 200 毫秒 | 
| receiveMessageVisibilityTimeout | 訊息的可見性逾時。如果取消設定，則會使用佇列的預設值。 | 佇列的預設 | 
| receiveMessageMinWaitDuration | receiveMessage 請求的最短等待時間。避免將 設定為 0 以防止 CPU 浪費。 | 50 毫秒 | 
| receiveMessageSystemAttributeNames | 要請求receiveMessage呼叫[的系統屬性名稱](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/model/MessageSystemAttributeName.html)清單。 | 無 | 
| receiveMessageAttributeNames | 要請求receiveMessage呼叫的[屬性名稱](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-message-metadata.html#sqs-message-attributes)清單。 | 無 | 

# 使用 Amazon Simple Queue Service 訊息佇列
<a name="examples-sqs-message-queues"></a>

*訊息佇列*是用於可靠傳送訊息的邏輯容器 Amazon Simple Queue Service。有兩種佇列類型：*標準*和*先進先出* (FIFO)。若要進一步了解佇列和這些類型之間的差異，請參閱 [Amazon Simple Queue Service 開發人員指南](https://docs.aws.amazon.com//AWSSimpleQueueService/latest/SQSDeveloperGuide/welcome.html)。

本主題說明如何使用 建立、列出、刪除和取得 Amazon Simple Queue Service 佇列的 URL 適用於 Java 的 AWS SDK。

下列範例中使用的 `sqsClient` 變數可從下列程式碼片段建立。

```
SqsClient sqsClient = SqsClient.create();
```

當您`SqsClient`使用靜態`create()`方法建立 時，開發套件會使用[預設區域提供者鏈](region-selection.md#default-region-provider-chain)來設定區域，並使用預設登入資料[提供者鏈來設定登入資料](credentials-chain.md)。

## 建立佇列
<a name="sqs-create-queue"></a>

使用 `SqsClient’s``createQueue`方法，並提供描述佇列參數的`[CreateQueueRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/model/CreateQueueRequest.html)`物件，如下列程式碼片段所示。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sqs.SqsClient;
import software.amazon.awssdk.services.sqs.model.*;
import java.util.List;
```

 **Code** 

```
            CreateQueueRequest createQueueRequest = CreateQueueRequest.builder()
                .queueName(queueName)
                .build();

            sqsClient.createQueue(createQueueRequest);
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/cf25559da654a7b74bec039c0ab9397dc5951dd4/javav2/example_code/sqs/src/main/java/com/example/sqs/SQSExample.java#L52)。

## 列出佇列
<a name="sqs-list-queues"></a>

若要列出您帳戶的 Amazon Simple Queue Service 佇列，請使用 `[ListQueuesRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/model/ListQueuesRequest.html)` 物件呼叫 `SqsClient’s``listQueues`方法。

當您使用不使用參數的 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/SqsClient.html#listQueues()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/SqsClient.html#listQueues())方法形式時，服務會傳回*所有佇列*，最多 1，000 個佇列。

您可以將佇列名稱字首提供給`[ListQueuesRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/model/ListQueuesRequest.html)`物件，將結果限制為符合該字首的佇列，如下列程式碼所示。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sqs.SqsClient;
import software.amazon.awssdk.services.sqs.model.*;
import java.util.List;
```

 **Code** 

```
        String prefix = "que";

        try {
            ListQueuesRequest listQueuesRequest = ListQueuesRequest.builder().queueNamePrefix(prefix).build();
            ListQueuesResponse listQueuesResponse = sqsClient.listQueues(listQueuesRequest);

            for (String url : listQueuesResponse.queueUrls()) {
                System.out.println(url);
            }

        } catch (SqsException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/cf25559da654a7b74bec039c0ab9397dc5951dd4/javav2/example_code/sqs/src/main/java/com/example/sqs/SQSExample.java#L79)。

## 取得佇列 URL
<a name="sqs-get-queue-url"></a>

下列程式碼說明如何透過使用 `[GetQueueUrlRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/model/GetQueueUrlRequest.html)` 物件呼叫 `SqsClient’s``getQueueUrl`方法來取得佇列的 URL。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sqs.SqsClient;
import software.amazon.awssdk.services.sqs.model.*;
import java.util.List;
```

 **Code** 

```
            GetQueueUrlResponse getQueueUrlResponse =
                sqsClient.getQueueUrl(GetQueueUrlRequest.builder().queueName(queueName).build());
            String queueUrl = getQueueUrlResponse.queueUrl();
            return queueUrl;
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/7486a1a092aa8e16a21698ef26f9d524fef62e55/javav2/example_code/sqs/src/main/java/com/example/sqs/SQSExample.java#L70)。

## 刪除佇列
<a name="sqs-delete-queue"></a>

提供`[DeleteQueueRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/model/DeleteQueueRequest.html)`物件的佇列 [URL](#sqs-get-queue-url)。然後呼叫 `SqsClient’s``deleteQueue`方法來刪除佇列，如下列程式碼所示。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sqs.SqsClient;
import software.amazon.awssdk.services.sqs.model.*;
import java.util.List;
```

 **Code** 

```
    public static void deleteSQSQueue(SqsClient sqsClient, String queueName) {

        try {

            GetQueueUrlRequest getQueueRequest = GetQueueUrlRequest.builder()
                    .queueName(queueName)
                    .build();

            String queueUrl = sqsClient.getQueueUrl(getQueueRequest).queueUrl();

            DeleteQueueRequest deleteQueueRequest = DeleteQueueRequest.builder()
                    .queueUrl(queueUrl)
                    .build();

            sqsClient.deleteQueue(deleteQueueRequest);

        } catch (SqsException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/6240df86c5f17eae1e23d1139d1435c7dc4b2a11/javav2/example_code/sqs/src/main/java/com/example/sqs/DeleteQueue.java#L48)。

## 其他資訊
<a name="more-information"></a>
+  Amazon Simple Queue Service API 參考中的 [CreateQueue](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_CreateQueue.html) 
+  Amazon Simple Queue Service API 參考中的 [GetQueueUrl](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_GetQueueUrl.html) 
+  Amazon Simple Queue Service API 參考中的 [ListQueues](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_ListQueues.html) 
+  Amazon Simple Queue Service API 參考中的 [DeleteQueue](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_DeleteQueue.html) 

# 傳送、接收和刪除 Amazon Simple Queue Service 訊息
<a name="examples-sqs-messages"></a>

訊息是資料片段，可透過分散式元件傳送和接收。訊息一律使用 [SQS 佇列](examples-sqs-message-queues.md)來傳送。

下列範例中使用的 `sqsClient` 變數可從下列程式碼片段建立。

```
SqsClient sqsClient = SqsClient.create();
```

當您`SqsClient`使用靜態`create()`方法建立 時，開發套件會使用[預設區域提供者鏈](region-selection.md#default-region-provider-chain)來設定區域，並使用預設登入資料[提供者鏈來設定登入資料](credentials-chain.md)。

## 傳送訊息
<a name="sqs-message-send"></a>

呼叫 SqsClient 用戶端`sendMessage`方法，將單一訊息新增至 Amazon Simple Queue Service 佇列。提供 [SendMessageRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/model/SendMessageRequest.html) 物件，其中包含佇列的 [URL](examples-sqs-message-queues.md#sqs-get-queue-url)、訊息本文，以及選用的延遲值 (以秒為單位)。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sqs.SqsClient;
import software.amazon.awssdk.services.sqs.model.*;
import java.util.List;
```

 **Code** 

```
            sqsClient.sendMessage(SendMessageRequest.builder()
                .queueUrl(queueUrl)
                .messageBody("Hello world!")
                .delaySeconds(10)
                .build());

            sqsClient.sendMessage(sendMsgRequest);
```

## 在請求中傳送多則訊息
<a name="sqs-messages-send-multiple"></a>

使用 SqsClient `sendMessageBatch`方法在單一請求中傳送多個訊息。這個方法採用 [SendMessageBatchRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/model/SendMessageBatchRequest.html)，其中包含佇列 URL 和要傳送的訊息清單。(每個訊息都是一個 [SendMessageBatchRequestEntry](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/model/SendMessageBatchRequestEntry.html)。) 您也可以在訊息上設定延遲值，延遲傳送特定的訊息。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sqs.SqsClient;
import software.amazon.awssdk.services.sqs.model.*;
import java.util.List;
```

 **Code** 

```
            SendMessageBatchRequest sendMessageBatchRequest = SendMessageBatchRequest.builder()
                .queueUrl(queueUrl)
                .entries(SendMessageBatchRequestEntry.builder().id("id1").messageBody("Hello from msg 1").build(),
                        SendMessageBatchRequestEntry.builder().id("id2").messageBody("msg 2").delaySeconds(10).build())
                .build();
            sqsClient.sendMessageBatch(sendMessageBatchRequest);
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/cf25559da654a7b74bec039c0ab9397dc5951dd4/javav2/example_code/sqs/src/main/java/com/example/sqs/SQSExample.java#L133)。

## 擷取訊息
<a name="sqs-messages-receive"></a>

呼叫 SqsClient `receiveMessage`方法，擷取目前佇列中的任何訊息。這個方法採用 [ReceiveMessageRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/model/ReceiveMessageRequest.html)，其中包含佇列 URL。您也可以指定要傳回的最大訊息數量。訊息會以 [Message](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/model/Message.html) 物件清單的形式傳回。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sqs.SqsClient;
import software.amazon.awssdk.services.sqs.model.*;
import java.util.List;
```

 **Code** 

```
        try {
            ReceiveMessageRequest receiveMessageRequest = ReceiveMessageRequest.builder()
                .queueUrl(queueUrl)
                .maxNumberOfMessages(5)
                .build();
            List<Message> messages = sqsClient.receiveMessage(receiveMessageRequest).messages();
            return messages;
        } catch (SqsException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
        return null;
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/cf25559da654a7b74bec039c0ab9397dc5951dd4/javav2/example_code/sqs/src/main/java/com/example/sqs/SQSExample.java#L148)。

## 在收到訊息後刪除訊息
<a name="sqs-messages-delete"></a>

收到訊息並處理其內容後，將訊息的接收控點和佇列 URL 傳送至 `SqsClient's`[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/SqsClient.html#deleteMessage(software.amazon.awssdk.services.sqs.model.DeleteMessageRequest)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/SqsClient.html#deleteMessage(software.amazon.awssdk.services.sqs.model.DeleteMessageRequest))方法，從佇列刪除訊息。

 **匯入** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sqs.SqsClient;
import software.amazon.awssdk.services.sqs.model.*;
import java.util.List;
```

 **Code** 

```
        try {
            for (Message message : messages) {
                DeleteMessageRequest deleteMessageRequest = DeleteMessageRequest.builder()
                        .queueUrl(queueUrl)
                        .receiptHandle(message.receiptHandle())
                        .build();
                sqsClient.deleteMessage(deleteMessageRequest);
            }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/cf25559da654a7b74bec039c0ab9397dc5951dd4/javav2/example_code/sqs/src/main/java/com/example/sqs/SQSExample.java#L187)。

## 詳細資訊
<a name="more-info"></a>
+  《 [Amazon Simple Queue Service 開發人員指南》中的佇列如何運作](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-basic-architecture.html) Amazon Simple Queue Service 
+  Amazon Simple Queue Service API 參考中的 [SendMessage](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_SendMessage.html) 
+  Amazon Simple Queue Service API 參考中的 [SendMessageBatch](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_SendMessageBatch.html) 
+  Amazon Simple Queue Service API 參考中的 [ReceiveMessage](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_ReceiveMessage.html) 
+  Amazon Simple Queue Service API 參考中的 [DeleteMessage](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_DeleteMessage.html) 

# 使用 Amazon Transcribe
<a name="examples-transcribe"></a>

以下範例顯示使用 Amazon Transcribe的雙向串流如何運作。雙向串流隱含表示資料的串流會前往服務，並即時接收回來。此範例使用 Amazon Transcribe 串流轉錄來傳送音訊串流，並即時接收轉錄文字的串流。

請參閱《 Amazon Transcribe 開發人員指南》中的[串流轉錄](https://docs.aws.amazon.com//transcribe/latest/dg/streaming.html)，以進一步了解此功能。

請參閱《 Amazon Transcribe 開發人員指南[》中的入門](https://docs.aws.amazon.com//transcribe/latest/dg/getting-started.html)以開始使用 Amazon Transcribe。

## 設定麥克風
<a name="set-up-the-microphone"></a>

此程式碼使用 javax.sound.sampled 套件，從輸入裝置串流音訊。

 **Code** 

```
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.TargetDataLine;

public class Microphone {

    public static TargetDataLine get() throws Exception {
        AudioFormat format = new AudioFormat(16000, 16, 1, true, false);
        DataLine.Info datalineInfo = new DataLine.Info(TargetDataLine.class, format);

        TargetDataLine dataLine = (TargetDataLine) AudioSystem.getLine(datalineInfo);
        dataLine.open(format);

        return dataLine;
    }
}
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/ac748d8ef99cd17e297cb74fe13aa671e2679088/javav2/example_code/transcribe/src/main/java/com/amazonaws/transcribe/Microphone.java)。

## 建立發佈者
<a name="create-a-publisher"></a>

此程式碼實作從音訊串流發佈 Amazon Transcribe 音訊資料的發佈者。

 **Code** 

```
package com.amazonaws.transcribe;

import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import software.amazon.awssdk.core.SdkBytes;
import software.amazon.awssdk.services.transcribestreaming.model.AudioEvent;
import software.amazon.awssdk.services.transcribestreaming.model.AudioStream;
import software.amazon.awssdk.services.transcribestreaming.model.TranscribeStreamingException;


public class AudioStreamPublisher implements Publisher<AudioStream> {
    private final InputStream inputStream;

    public AudioStreamPublisher(InputStream inputStream) {
        this.inputStream = inputStream;
    }

    @Override
    public void subscribe(Subscriber<? super AudioStream> s) {
        s.onSubscribe(new SubscriptionImpl(s, inputStream));
    }

    private class SubscriptionImpl implements Subscription {
        private static final int CHUNK_SIZE_IN_BYTES = 1024 * 1;
        private ExecutorService executor = Executors.newFixedThreadPool(1);
        private AtomicLong demand = new AtomicLong(0);

        private final Subscriber<? super AudioStream> subscriber;
        private final InputStream inputStream;

        private SubscriptionImpl(Subscriber<? super AudioStream> s, InputStream inputStream) {
            this.subscriber = s;
            this.inputStream = inputStream;
        }

        @Override
        public void request(long n) {
            if (n <= 0) {
                subscriber.onError(new IllegalArgumentException("Demand must be positive"));
            }

            demand.getAndAdd(n);

            executor.submit(() -> {
                try {
                    do {
                        ByteBuffer audioBuffer = getNextEvent();
                        if (audioBuffer.remaining() > 0) {
                            AudioEvent audioEvent = audioEventFromBuffer(audioBuffer);
                            subscriber.onNext(audioEvent);
                        } else {
                            subscriber.onComplete();
                            break;
                        }
                    } while (demand.decrementAndGet() > 0);
                } catch (TranscribeStreamingException e) {
                    subscriber.onError(e);
                }
            });
        }

        @Override
        public void cancel() {

        }

        private ByteBuffer getNextEvent() {
            ByteBuffer audioBuffer;
            byte[] audioBytes = new byte[CHUNK_SIZE_IN_BYTES];

            int len = 0;
            try {
                len = inputStream.read(audioBytes);

                if (len <= 0) {
                    audioBuffer = ByteBuffer.allocate(0);
                } else {
                    audioBuffer = ByteBuffer.wrap(audioBytes, 0, len);
                }
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }

            return audioBuffer;
        }

        private AudioEvent audioEventFromBuffer(ByteBuffer bb) {
            return AudioEvent.builder()
                    .audioChunk(SdkBytes.fromByteBuffer(bb))
                    .build();
        }
    }
}
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/ac748d8ef99cd17e297cb74fe13aa671e2679088/javav2/example_code/transcribe/src/main/java/com/amazonaws/transcribe/AudioStreamPublisher.java)。

## 建立用戶端並啟動串流
<a name="create-the-client-and-start-the-stream"></a>

在主要方法中，建立請求物件，開始音訊輸入串流並使用音訊輸入將發佈者執行個體化。

您也必須建立 [StartStreamTranscriptionResponseHandler](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/transcribestreaming/model/StartStreamTranscriptionResponseHandler.html)，以指定如何處理來自 的回應 Amazon Transcribe。

然後，使用 TranscribeStreamingAsyncClient 的 `startStreamTranscription`方法來啟動雙向串流。

 **匯入** 

```
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.TargetDataLine;
import javax.sound.sampled.AudioInputStream;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.transcribestreaming.TranscribeStreamingAsyncClient;
import software.amazon.awssdk.services.transcribestreaming.model.TranscribeStreamingException ;
import software.amazon.awssdk.services.transcribestreaming.model.StartStreamTranscriptionRequest;
import software.amazon.awssdk.services.transcribestreaming.model.MediaEncoding;
import software.amazon.awssdk.services.transcribestreaming.model.LanguageCode;
import software.amazon.awssdk.services.transcribestreaming.model.StartStreamTranscriptionResponseHandler;
import software.amazon.awssdk.services.transcribestreaming.model.TranscriptEvent;
```

 **Code** 

```
    public static void convertAudio(TranscribeStreamingAsyncClient client) throws Exception {

        try {

            StartStreamTranscriptionRequest request = StartStreamTranscriptionRequest.builder()
                    .mediaEncoding(MediaEncoding.PCM)
                    .languageCode(LanguageCode.EN_US)
                    .mediaSampleRateHertz(16_000).build();

            TargetDataLine mic = Microphone.get();
            mic.start();

            AudioStreamPublisher publisher = new AudioStreamPublisher(new AudioInputStream(mic));

            StartStreamTranscriptionResponseHandler response =
                    StartStreamTranscriptionResponseHandler.builder().subscriber(e -> {
                        TranscriptEvent event = (TranscriptEvent) e;
                        event.transcript().results().forEach(r -> r.alternatives().forEach(a -> System.out.println(a.transcript())));
                    }).build();

            // Keeps Streaming until you end the Java program
            client.startStreamTranscription(request, publisher, response);

        } catch (TranscribeStreamingException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
         }
    }
```

請參閱 GitHub 上的[完整範例](https://github.com/awsdocs/aws-doc-sdk-examples/blob/ac748d8ef99cd17e297cb74fe13aa671e2679088/javav2/example_code/transcribe/src/main/java/com/amazonaws/transcribe/BidirectionalStreaming.java)。

## 其他資訊
<a name="more-info"></a>
+  Amazon Transcribe 開發人員指南中的[運作方式](https://docs.aws.amazon.com//transcribe/latest/dg/how-it-works.html)。
+  《 開發人員指南》中的 Amazon Transcribe [串流音訊入門](https://docs.aws.amazon.com//transcribe/latest/dg/getting-started.html)。