

# 处理本地二级索引：Java
<a name="LSIJavaDocumentAPI"></a>

您可以使用 适用于 Java 的 AWS SDK 文档 API 创建具有一个或多个本地二级索引的 Amazon DynamoDB 表、描述表中的索引，以及使用索引执行查询。

下面是使用 适用于 Java 的 AWS SDK 文档 API 执行表操作的常见步骤。

1. 创建 `DynamoDB` 类的实例。

1. 通过创建对应的请求对象，为操作提供必需参数和可选参数。

1. 调用您在前面步骤中创建的客户端提供的适当方法。

**Topics**
+ [创建具有本地二级索引的表](#LSIJavaDocumentAPI.CreateTableWithIndex)
+ [描述具有本地二级索引的表](#LSIJavaDocumentAPI.DescribeTableWithIndex)
+ [查询本地二级索引](#LSIJavaDocumentAPI.QueryAnIndex)
+ [示例：使用 Java 文档 API 的本地二级索引](LSIJavaDocumentAPI.Example.md)

## 创建具有本地二级索引的表
<a name="LSIJavaDocumentAPI.CreateTableWithIndex"></a>

本地二级索引必须在您创建表的同时创建。为此，请使用 `createTable` 方法并为一个或多个本地二级索引提供您的规范。以下 Java 代码示例创建一个包含音乐精选中歌曲信息的表。分区键为 `Artist`，排序键为 `SongTitle`。`AlbumTitleIndex` 这一二级索引可以按专辑名称进行查询。

下面是使用 DynamoDB 文档 API 创建具有本地二级索引的表的步骤。

1. 创建 `DynamoDB` 类的实例。

1. 创建 `CreateTableRequest` 类实例，以提供请求信息。

   您必须提供表名称、主键以及预配置吞吐量值。对于本地二级索引，您必须提供索引名称、索引排序键的名称和数据类型、索引的键架构以及属性投影。

1. 以参数形式提供请求对象，以调用 `createTable` 方法。

以下 Java 代码示例演示了上述步骤。该代码创建表 (`Music`)，在 `AlbumTitle` 属性上具有二级索引。投影到索引的属性只有表的分区键、排序键以及索引排序键。

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
DynamoDB dynamoDB = new DynamoDB(client);

String tableName = "Music";

CreateTableRequest createTableRequest = new CreateTableRequest().withTableName(tableName);

//ProvisionedThroughput
createTableRequest.setProvisionedThroughput(new ProvisionedThroughput().withReadCapacityUnits((long)5).withWriteCapacityUnits((long)5));

//AttributeDefinitions
ArrayList<AttributeDefinition> attributeDefinitions= new ArrayList<AttributeDefinition>();
attributeDefinitions.add(new AttributeDefinition().withAttributeName("Artist").withAttributeType("S"));
attributeDefinitions.add(new AttributeDefinition().withAttributeName("SongTitle").withAttributeType("S"));
attributeDefinitions.add(new AttributeDefinition().withAttributeName("AlbumTitle").withAttributeType("S"));

createTableRequest.setAttributeDefinitions(attributeDefinitions);

//KeySchema
ArrayList<KeySchemaElement> tableKeySchema = new ArrayList<KeySchemaElement>();
tableKeySchema.add(new KeySchemaElement().withAttributeName("Artist").withKeyType(KeyType.HASH));  //Partition key
tableKeySchema.add(new KeySchemaElement().withAttributeName("SongTitle").withKeyType(KeyType.RANGE));  //Sort key

createTableRequest.setKeySchema(tableKeySchema);

ArrayList<KeySchemaElement> indexKeySchema = new ArrayList<KeySchemaElement>();
indexKeySchema.add(new KeySchemaElement().withAttributeName("Artist").withKeyType(KeyType.HASH));  //Partition key
indexKeySchema.add(new KeySchemaElement().withAttributeName("AlbumTitle").withKeyType(KeyType.RANGE));  //Sort key

Projection projection = new Projection().withProjectionType(ProjectionType.INCLUDE);
ArrayList<String> nonKeyAttributes = new ArrayList<String>();
nonKeyAttributes.add("Genre");
nonKeyAttributes.add("Year");
projection.setNonKeyAttributes(nonKeyAttributes);

LocalSecondaryIndex localSecondaryIndex = new LocalSecondaryIndex()
    .withIndexName("AlbumTitleIndex").withKeySchema(indexKeySchema).withProjection(projection);

ArrayList<LocalSecondaryIndex> localSecondaryIndexes = new ArrayList<LocalSecondaryIndex>();
localSecondaryIndexes.add(localSecondaryIndex);
createTableRequest.setLocalSecondaryIndexes(localSecondaryIndexes);

Table table = dynamoDB.createTable(createTableRequest);
System.out.println(table.getDescription());
```

您必须等待 DynamoDB 创建该表并将表的状态设置为 `ACTIVE`。然后，您就可以开始在表中添加数据项目。

## 描述具有本地二级索引的表
<a name="LSIJavaDocumentAPI.DescribeTableWithIndex"></a>

要获取表上有关本地二级索引的信息，请使用 `describeTable` 方法。对于每个索引，您都可以查看其名称、键架构和投影的属性。

以下是使用 适用于 Java 的 AWS SDK 文档 API 访问表的本地二级索引信息的步骤。

1. 创建 `DynamoDB` 类的实例。

1. 创建 `Table` 类的实例。您必须提供表名称。

1. 调用 `describeTable` 对象上的 `Table` 方法。

以下 Java 代码示例演示了上述步骤。

**Example**  

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
DynamoDB dynamoDB = new DynamoDB(client);

String tableName = "Music";

Table table = dynamoDB.getTable(tableName);

TableDescription tableDescription = table.describe();

List<LocalSecondaryIndexDescription> localSecondaryIndexes 
    = tableDescription.getLocalSecondaryIndexes();

// This code snippet will work for multiple indexes, even though
// there is only one index in this example.

Iterator<LocalSecondaryIndexDescription> lsiIter = localSecondaryIndexes.iterator();
while (lsiIter.hasNext()) {

    LocalSecondaryIndexDescription lsiDescription = lsiIter.next();
    System.out.println("Info for index " + lsiDescription.getIndexName() + ":");
    Iterator<KeySchemaElement> kseIter = lsiDescription.getKeySchema().iterator();
    while (kseIter.hasNext()) {
        KeySchemaElement kse = kseIter.next();
        System.out.printf("\t%s: %s\n", kse.getAttributeName(), kse.getKeyType());
    }
    Projection projection = lsiDescription.getProjection();
    System.out.println("\tThe projection type is: " + projection.getProjectionType());
    if (projection.getProjectionType().toString().equals("INCLUDE")) {
        System.out.println("\t\tThe non-key projected attributes are: " + projection.getNonKeyAttributes());
    }
}
```

## 查询本地二级索引
<a name="LSIJavaDocumentAPI.QueryAnIndex"></a>

您可以对本地二级索引使用 `Query` 操作，基本与对表执行 `Query` 操作一样。您需要指定索引名称、索引排序键的查询条件以及要返回的属性。在本示例中，索引为 `AlbumTitleIndex`，索引排序键为 `AlbumTitle`。

要返回的只包含投影到索引的属性。您也可以修改此查询，让返回结果中也包含非键属性，但是这样会导致表抓取活动的成本相对较高的。有关表获取的更多信息，请参阅 [属性投影](LSI.md#LSI.Projections)。

以下是使用 适用于 Java 的 AWS SDK 文档 API 查询本地二级索引的步骤。

1. 创建 `DynamoDB` 类的实例。

1. 创建 `Table` 类的实例。您必须提供表名称。

1. 创建 `Index` 类的实例。您必须提供索引名称。

1. 调用 `query` 类的 `Index` 方法。

以下 Java 代码示例演示了上述步骤。

**Example**  

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
DynamoDB dynamoDB = new DynamoDB(client);

String tableName = "Music";

Table table = dynamoDB.getTable(tableName);
Index index = table.getIndex("AlbumTitleIndex");

QuerySpec spec = new QuerySpec()
    .withKeyConditionExpression("Artist = :v_artist and AlbumTitle = :v_title")
    .withValueMap(new ValueMap()
        .withString(":v_artist", "Acme Band")
        .withString(":v_title", "Songs About Life"));

ItemCollection<QueryOutcome> items = index.query(spec);

Iterator<Item> itemsIter = items.iterator();

while (itemsIter.hasNext()) {
    Item item = itemsIter.next();
    System.out.println(item.toJSONPretty());
}
```

### 对本地二级索引的一致性读取
<a name="LSIJavaDocumentAPI.ConsistentReads"></a>

与仅支持最终一致性读取的全局二级索引不同，本地二级索引支持最终一致性读取和强一致性读取。从本地二级索引进行的强一致性读取始终返回上次更新的值。如果查询需要从基表中提取其他属性，则这些提取的属性同样与索引保持一致。

默认情况下，`Query` 使用最终一致性读取。要请求强一致性读取，请在 `QuerySpec` 中将 `ConsistentRead` 设置为 `true`。以下示例使用强一致性读取查询 `AlbumTitleIndex`：

**Example**  

```
QuerySpec spec = new QuerySpec()
    .withKeyConditionExpression("Artist = :v_artist and AlbumTitle = :v_title")
    .withValueMap(new ValueMap()
        .withString(":v_artist", "Acme Band")
        .withString(":v_title", "Songs About Life"))
    .withConsistentRead(true);
```

**注意**  
对于返回的每 4 KB 数据（向上取整），一个强一致性读取消耗一个读取容量单位，而最终一致性读取仅消耗一半的读取容量单位。例如，返回 9 KB 数据的强一致性读取消耗 3 个读取容量单位（9 KB/4 KB = 2.25，向上取整为 3），而使用最终一致性读取的相同查询消耗 1.5 个读取容量单位。如果您的应用程序能够容忍读取可能稍微陈旧的数据，请使用最终一致性读取来减少读取容量使用量。有关更多信息，请参阅 [读取容量单位](LSI.md#LSI.ThroughputConsiderations.Reads)。