写入数据

表格存储(Tablestore)提供了单行和批量的方式写入数据到数据表,您可以结合实际场景选择适合的数据写入方式。本文为您介绍如何通过Java SDK写入表格存储数据。

前提条件

  • 初始化OTSClient。具体操作,请参见初始化OTSClient

  • 在表格存储的实例中创建数据表。

写入方式

表格存储提供单行写入、单行更新和批量写入数据功能。不同写入方式的区别和适用场景请参见下表。

写入方式

说明

适用场景

写入单行数据

调用 PutRow 接口新写入一行数据。

适用于写入少量数据的场景。

更新单行数据

调用 UpdateRow 接口更新一行数据。

适用于更新少量数据的场景。

批量写入数据

调用 BatchWriteRow 接口在一次请求中进行批量写入操作或者一次对多张表进行写入。

适用于增删改大量数据或者同时需要进行数据增删改操作的场景。

参数说明

数据写入的相关参数说明如下表所示。

参数

说明

tableName

数据表的名称。

primaryKey

数据表的主键信息。包括主键列名、主键类型和主键值。

重要
  • 设置的主键个数和类型必须与数据表的主键个数和类型保持一致。

  • 主键为自增列时,只需将自增列的值设置为占位符。具体方式,请参见主键列自增

column

属性列信息。包括属性列名称、属性列的值、属性列数据类型(可选)和时间戳(可选)。

  • 属性列名称。由英文字符(a~z)或(A~Z)、数字(0~9)和下划线(_)组成,首字母不能为数字,大小写敏感,长度在1~255个字符之间。

  • 属性列数据类型。支持字符串、整型、二进制、浮点数和布尔值。

  • 时间戳即数据的版本号。默认由系统自动生成,也可以自行指定。更多关于版本号的信息,请参见数据版本和生命周期

说明
  • 删除属性列时,只需要设置属性列名称。

  • 删除属性列特定版本的数据时,需要设置属性列名称和时间戳。

  • 删除一行数据的全部属性列不等于删除该行。如果需要删除该行,请使用DeleteRow接口操作,具体操作请参见删除数据

condition

写入条件。包括行存在性条件和列判断条件,更多信息,请参见条件更新

写入单行数据

调用PutRow接口新写入一行数据。如果该行已存在,默认会先删除原行数据(原行的所有列以及所有版本的数据),再写入新的行数据。

说明

本文示例中的pkValue均表示主键列值,使用时请根据实际填写具体数据。写入数据时,您需要指定完整的主键以及需要写入的属性列信息。您也可以根据需要配置数据写入条件,例如设置只有在行不存在时才写入数据。

以下示例代码为您展示如何使用系统自动生成的数据版本号、自定义数据版本号以及如何使用写入条件。

写入数据时系统自动生成数据版本号

以下示例代码新增了一行数据,写入10个属性列,每列写入1个版本,由系统自动生成数据的版本号(时间戳)。

private static void putRow(SyncClient client, String pkValue) {
    //构造主键。
    PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
    primaryKeyBuilder.addPrimaryKeyColumn("your_primaryKey", PrimaryKeyValue.fromString(pkValue));
    PrimaryKey primaryKey = primaryKeyBuilder.build();
    //设置数据表名称。
    RowPutChange rowPutChange = new RowPutChange("your_tableName", primaryKey);

    //加入一些属性列。
    for (int i = 0; i < 10; i++) {
        rowPutChange.addColumn(new Column("Col" + i, ColumnValue.fromLong(i)));
    }

    client.putRow(new PutRowRequest(rowPutChange));
}

插入数据时自定义数据版本号

以下示例代码新增了一行数据,写入10个属性列,每列写入3个版本,并且自定义数据的版本号。

private static void putRow(SyncClient client, String pkValue) {
    //构造主键。
    PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
    primaryKeyBuilder.addPrimaryKeyColumn("your_primaryKey", PrimaryKeyValue.fromString(pkValue));
    PrimaryKey primaryKey = primaryKeyBuilder.build();
    //设置数据表名称。
    RowPutChange rowPutChange = new RowPutChange("your_tableName", primaryKey);

    //加入一些属性列。
    long ts = System.currentTimeMillis();
    for (int i = 0; i < 10; i++) {
        for (int j = 0; j < 3; j++) {
            rowPutChange.addColumn(new Column("Col" + i, ColumnValue.fromLong(j), ts + j));
        }
    }

    client.putRow(new PutRowRequest(rowPutChange));
}

插入数据时使用行条件

以下示例代码在原行不存在时新增一条数据,写入10个属性列,每列写入3个版本,并且自定义数据的版本号(时间戳)。

private static void putRow(SyncClient client, String pkValue) {
    //构造主键。
    PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
    primaryKeyBuilder.addPrimaryKeyColumn("your_primaryKey", PrimaryKeyValue.fromString(pkValue));
    PrimaryKey primaryKey = primaryKeyBuilder.build();
    //设置数据表名称。
    RowPutChange rowPutChange = new RowPutChange("your_tableName", primaryKey);

    //设置条件更新,行条件检查为原行不存在时才写入数据。
    rowPutChange.setCondition(new Condition(RowExistenceExpectation.EXPECT_NOT_EXIST));

    //加入一些属性列。
    long ts = System.currentTimeMillis();
    for (int i = 0; i < 10; i++) {
        for (int j = 0; j < 3; j++) {
            rowPutChange.addColumn(new Column("Col" + i, ColumnValue.fromLong(j), ts + j));
        }
    }

    client.putRow(new PutRowRequest(rowPutChange));
}

插入数据时同时使用列和行条件

以下示例代码在原行存在且Col0列的值大于100时写入数据,写入10个属性列,每列写入3个版本,并且自定义数据的版本号(时间戳)。

private static void putRow(SyncClient client, String pkValue) {
    //构造主键。
    PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
    primaryKeyBuilder.addPrimaryKeyColumn("your_primaryKey", PrimaryKeyValue.fromString(pkValue));
    PrimaryKey primaryKey = primaryKeyBuilder.build();
    //设置数据表名称。
    RowPutChange rowPutChange = new RowPutChange("your_tableName", primaryKey);

    //设置条件更新,期望原行存在且原行Col0列的值大于100时才写入数据。
    Condition condition = new Condition(RowExistenceExpectation.EXPECT_EXIST);
    condition.setColumnCondition(new SingleColumnValueCondition("Col0",
            SingleColumnValueCondition.CompareOperator.GREATER_THAN, ColumnValue.fromLong(100)));
    rowPutChange.setCondition(condition);

    //加入一些属性列。
    long ts = System.currentTimeMillis();
    for (int i = 0; i < 10; i++) {
        for (int j = 0; j < 3; j++) {
            rowPutChange.addColumn(new Column("Col" + i, ColumnValue.fromLong(j), ts + j));
        }
    }

    client.putRow(new PutRowRequest(rowPutChange));
}

更新单行数据

调用UpdateRow接口更新一行数据。可以更新属性列的值、增加和删除属性列、删除属性列指定版本的数据。如果更新的行不存在,则新增一行数据。

说明

当请求中只包含删除指定的属性列并且该行不存在时,则该请求不会新增行数据。

以下示例代码均更新了一些列、删除Col1列的某一数据版本,并删除了Col0列,区别在于是否使用了写入条件。

更新数据时不使用条件

以下示例代码在更新数据时不使用写入条件。

private static void updateRow(SyncClient client, String pkValue) {
    //构造主键。
    PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
    primaryKeyBuilder.addPrimaryKeyColumn("your_primaryKey", PrimaryKeyValue.fromString(pkValue));
    PrimaryKey primaryKey = primaryKeyBuilder.build();
    //设置数据表名称。
    RowUpdateChange rowUpdateChange = new RowUpdateChange("your_tableName", primaryKey);

    //更新一些列。
    for (int i = 0; i < 10; i++) {
        rowUpdateChange.put(new Column("Col" + i, ColumnValue.fromLong(i)));
    }

    //删除某列的某一版本。
    rowUpdateChange.deleteColumn("Col1", 1465373223000L);

    //删除某一列。
    rowUpdateChange.deleteColumns("Col0");

    client.updateRow(new UpdateRowRequest(rowUpdateChange));
}

更新数据时使用列和行条件

以下示例代码在原行存在且Col0列的值大于100时才更新数据。

private static void updateRow(SyncClient client, String pkValue) {
    //构造主键。
    PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
    primaryKeyBuilder.addPrimaryKeyColumn("your_primaryKey", PrimaryKeyValue.fromString(pkValue));
    PrimaryKey primaryKey = primaryKeyBuilder.build();
    //设置数据表名称。
    RowUpdateChange rowUpdateChange = new RowUpdateChange("your_tableName", primaryKey);

    //设置条件更新,期望原行存在且Col0列的值大于100时更新数据。
    Condition condition = new Condition(RowExistenceExpectation.EXPECT_EXIST);
    condition.setColumnCondition(new SingleColumnValueCondition("Col0",
            SingleColumnValueCondition.CompareOperator.GREATER_THAN, ColumnValue.fromLong(100)));
    rowUpdateChange.setCondition(condition);

    //更新一些列。
    for (int i = 0; i < 10; i++) {
        rowUpdateChange.put(new Column("Col" + i, ColumnValue.fromLong(i)));
    }

    //删除某列的某一版本。
    rowUpdateChange.deleteColumn("Col1", 1465373223000L);

    //删除某一列。
    rowUpdateChange.deleteColumns("Col0");

    client.updateRow(new UpdateRowRequest(rowUpdateChange));
}

批量写入数据

调用BatchWriteRow接口在一次请求中进行批量写入操作或者一次对多张表进行写入。

BatchWriteRow操作由多个PutRow、UpdateRowDeleteRow子操作组成,构造子操作的过程与单独使用PutRow接口、UpdateRow接口和DeleteRow接口相同。各个子操作独立执行,表格存储会分别返回每个子操作的执行结果。

说明
  • 服务端检查到部分操作的参数错误时,BatchWriteRow接口会抛出参数错误异常,此时该请求中的所有操作都将不执行。

  • 批量写入过程中可能存在部分行写入失败的情况,请求返回的BatchWriteRowResponse包含了失败行的Index及错误信息。建议您检查返回值,可以通过BatchWriteRowResponseisAllSucceed方法判断批量写入是否全部成功。

以下示例代码的BatchWriteRow请求中包含2PutRow操作、1UpdateRow操作和1DeleteRow操作。

private static void batchWriteRow(SyncClient client) {
    BatchWriteRowRequest batchWriteRowRequest = new BatchWriteRowRequest();

    //构造第1个rowPutChange。
    PrimaryKeyBuilder pk1Builder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
    pk1Builder.addPrimaryKeyColumn("your_primaryKey", PrimaryKeyValue.fromString("pkValue1"));
    //设置数据表名称。
    RowPutChange rowPutChange1 = new RowPutChange("your_tableName", pk1Builder.build());
    //添加一些列。
    for (int i = 0; i < 10; i++) {
        rowPutChange1.addColumn(new Column("Col" + i, ColumnValue.fromLong(i)));
    }
    //添加到batch操作中。
    batchWriteRowRequest.addRowChange(rowPutChange1);

    //构造第2个rowPutChange。
    PrimaryKeyBuilder pk2Builder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
    pk2Builder.addPrimaryKeyColumn("your_primaryKey", PrimaryKeyValue.fromString("pkValue2"));
    //设置数据表名称。
    RowPutChange rowPutChange2 = new RowPutChange("your_tableName", pk2Builder.build());
    //添加一些列。
    for (int i = 0; i < 10; i++) {
        rowPutChange2.addColumn(new Column("Col" + i, ColumnValue.fromLong(i)));
    }
    //添加到batch操作中。
    batchWriteRowRequest.addRowChange(rowPutChange2);

    //构造rowUpdateChange。
    PrimaryKeyBuilder pk3Builder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
    pk3Builder.addPrimaryKeyColumn("your_primaryKey", PrimaryKeyValue.fromString("pkValue3"));
    //设置数据表名称。
    RowUpdateChange rowUpdateChange = new RowUpdateChange("your_tableName", pk3Builder.build());
    //添加一些列。
    for (int i = 0; i < 10; i++) {
        rowUpdateChange.put(new Column("Col" + i, ColumnValue.fromLong(i)));
    }
    //删除一列。
    rowUpdateChange.deleteColumns("Col0");
    //添加到batch操作中。
    batchWriteRowRequest.addRowChange(rowUpdateChange);

    //构造rowDeleteChange。
    PrimaryKeyBuilder pk4Builder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
    pk4Builder.addPrimaryKeyColumn("your_primaryKey", PrimaryKeyValue.fromString("pkValue4"));
    //设置数据表名称。
    RowDeleteChange rowDeleteChange = new RowDeleteChange("your_tableName", pk4Builder.build());
    //添加到batch操作中。
    batchWriteRowRequest.addRowChange(rowDeleteChange);

    BatchWriteRowResponse response = client.batchWriteRow(batchWriteRowRequest);

    System.out.println("是否全部成功:" + response.isAllSucceed());
    if (!response.isAllSucceed()) {
        for (BatchWriteRowResponse.RowResult rowResult : response.getFailedRows()) {
            System.out.println("失败的行:" + batchWriteRowRequest.getRowChange(rowResult.getTableName(), rowResult.getIndex()).getPrimaryKey());
            System.out.println("失败原因:" + rowResult.getError());
        }
        /**
         * 可以通过createRequestForRetry方法再构造一个请求对失败的行进行重试。此处只给出构造重试请求的部分。
         * 推荐的重试方法是使用SDK的自定义重试策略功能,支持对batch操作的部分行错误进行重试。设置重试策略后,调用接口处无需增加重试代码。
         */
        BatchWriteRowRequest retryRequest = batchWriteRowRequest.createRequestForRetry(response.getFailedRows());
    }
}

常见问题

相关文档

  • 如果您想了解更多表格存储数据操作的示例代码,请参见GitHub示例代码

  • BatchWriteRow接口单次只能写入200行数据,如果您需要高并发数据写入,请使用TableStoreWriter工具。更多信息,请参见使用TableStoreWriter并发写入数据

  • 如果您需要为在线应用提供实时统计功能,例如统计帖子的PV(页面浏览量)等,可以通过原子计数器实现。更多信息,请参见原子计数器

  • 如果您需要进行单行写或多行写的原子操作,可以通过局部事务实现。更多信息,请参见局部事务

  • 如果您需要了解表格存储各场景的应用案例,请参见快速玩转Tablestore入门与实战