Tablestore SDK for Java provides multi-row operations such as BatchWriteRow, BatchGetRow, GetRange, and createRangeIterator.

Prerequisites

  • The OTSClient instance is initialized. For more information, see Initialization.
  • A data table is created and data is written to the table.

BatchWriteRow

You can call this operation to write several rows of data to one or more tables by sending one request. The BatchWriteRow operation consists of PutRow, UpdateRow, and DeleteRow operations. The process of constructing a suboperation is the same as that of calling the PutRow, UpdateRow, and DeleteRow operations. BatchWriteRow supports conditional updates.

Each suboperation of BatchWriteRow is independently performed, and the result for each suboperation is independently returned.

When multiple rows are written, some rows may fail to be written. If this happens, Tablestore does not return exceptions, but information about the index and error messages of the failed rows is stored in BatchWriteRowResponse. Therefore, when you call the BatchWriteRow operation, you must check the return values. You can use the isAllSucceed method of BatchWriteRowResponse to check whether all operations are successful. If you do not check the return values, some operation failures may be ignored.

If the server detects that invalid parameters exist in some operations, the BatchWriteRow operation may return an exception about parameter errors before performing all the operations in the request.

  • Parameters

    For information about parameters, see Single-row operations.

  • Examples

    The following code provides an example on how to send a single BatchWriteRow request, which includes two PutRow operations, one UpdateRow operation, and one DeleteRow operation:

    private static void batchWriteRow(SyncClient client) {
        BatchWriteRowRequest batchWriteRowRequest = new BatchWriteRowRequest();
    
        // Construct rowPutChange1. 
        PrimaryKeyBuilder pk1Builder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
        pk1Builder.addPrimaryKeyColumn(PRIMARY_KEY_NAME, PrimaryKeyValue.fromString("pk1"));
        RowPutChange rowPutChange1 = new RowPutChange(TABLE_NAME, pk1Builder.build());
        // Add columns. 
        for (int i = 0; i < 10; i++) {
            rowPutChange1.addColumn(new Column("Col" + i, ColumnValue.fromLong(i)));
        }
        // Add rowPutChange1 to the batch operation. 
        batchWriteRowRequest.addRowChange(rowPutChange1);
    
        // Construct rowPutChange2. 
        PrimaryKeyBuilder pk2Builder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
        pk2Builder.addPrimaryKeyColumn(PRIMARY_KEY_NAME, PrimaryKeyValue.fromString("pk2"));
        RowPutChange rowPutChange2 = new RowPutChange(TABLE_NAME, pk2Builder.build());
        // Add columns. 
        for (int i = 0; i < 10; i++) {
            rowPutChange2.addColumn(new Column("Col" + i, ColumnValue.fromLong(i)));
        }
        // Add rowPutChange2 to the batch operation. 
        batchWriteRowRequest.addRowChange(rowPutChange2);
    
        // Construct rowUpdateChange. 
        PrimaryKeyBuilder pk3Builder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
        pk3Builder.addPrimaryKeyColumn(PRIMARY_KEY_NAME, PrimaryKeyValue.fromString("pk3"));
        RowUpdateChange rowUpdateChange = new RowUpdateChange(TABLE_NAME, pk3Builder.build());
        // Add columns. 
        for (int i = 0; i < 10; i++) {
            rowUpdateChange.put(new Column("Col" + i, ColumnValue.fromLong(i)));
        }
        // Delete a column. 
        rowUpdateChange.deleteColumns("Col10");
        // Add rowUpdateChange to the batch operation. 
        batchWriteRowRequest.addRowChange(rowUpdateChange);
    
        // Construct rowDeleteChange. 
        PrimaryKeyBuilder pk4Builder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
        pk4Builder.addPrimaryKeyColumn(PRIMARY_KEY_NAME, PrimaryKeyValue.fromString("pk4"));
        RowDeleteChange rowDeleteChange = new RowDeleteChange(TABLE_NAME, pk4Builder.build());
        // Add rowDeleteChange to the batch operation. 
        batchWriteRowRequest.addRowChange(rowDeleteChange);
    
        BatchWriteRowResponse response = client.batchWriteRow(batchWriteRowRequest);
    
        System.out.println("Whether all operations are successful:" + response.isAllSucceed());
        if (!response.isAllSucceed()) {
            for (BatchWriteRowResponse.RowResult rowResult : response.getFailedRows()) {
                System.out.println("Failed rows:" + batchWriteRowRequest.getRowChange(rowResult.getTableName(), rowResult.getIndex()).getPrimaryKey());
                System.out.println("Cause of failures:" + rowResult.getError());
            }
            /**
             * You can use the createRequestForRetry method to construct another request to retry the operations on failed rows. Only the retry request is constructed here. 
             * We recommend that you use the custom retry policy in Tablestore SDKs as the retry method. This feature allows you to retry failed rows after batch operations. After you set the retry policy, you do not need to add retry code when you call the operation. 
             */
            BatchWriteRowRequest retryRequest = batchWriteRowRequest.createRequestForRetry(response.getFailedRows());
        }
    }
                

    For the detailed sample code, visit BatchWriteRow@GitHub.

BatchGetRow

You can call this operation to read several rows of data from one or more tables by sending one request. The BatchGetRow operation consists of multiple GetRow operations. The process of constructing a suboperation is the same as that of calling the GetRow operation.

Note that BatchGetRow uses the same parameter configurations for all rows. For example, if ColumnsToGet is set to [colA], only the value of the colA column is read from all rows.

Each suboperation of BatchGetRow is independently performed, and the result for each suboperation is independently returned.

. When multiple rows are read, some rows may fail to be read. If this happens, Tablestore does not return exceptions, but information about the error messages of the failed rows is stored in BatchGetRowResponse. Therefore, when you call the BatchGetRow operation, you must check the return values. You can use the isAllSucceed method of BatchGetRowResponse to check whether all operations are successful or use the getFailedRows method of BatchGetRowResponse to obtain the information about failed rows.

  • Parameters

    For information about parameters, see Single-row operations.

  • Examples

    The following code provides an example on how to configure the version conditions, columns to be read, and filters to read 10 rows of data:

    private static void batchGetRow(SyncClient client) {
        MultiRowQueryCriteria multiRowQueryCriteria = new MultiRowQueryCriteria(TABLE_NAME);
        // Add 10 rows. 
        for (int i = 0; i < 10; i++) {
            PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
            primaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME, PrimaryKeyValue.fromString("pk" + i));
            PrimaryKey primaryKey = primaryKeyBuilder.build();
            multiRowQueryCriteria.addRow(primaryKey);
        }
        // Add conditions. 
        multiRowQueryCriteria.setMaxVersions(1);
        multiRowQueryCriteria.addColumnsToGet("Col0");
        multiRowQueryCriteria.addColumnsToGet("Col1");
        SingleColumnValueFilter singleColumnValueFilter = new SingleColumnValueFilter("Col0",
                SingleColumnValueFilter.CompareOperator.EQUAL, ColumnValue.fromLong(0));
        singleColumnValueFilter.setPassIfMissing(false);
        multiRowQueryCriteria.setFilter(singleColumnValueFilter);
    
        BatchGetRowRequest batchGetRowRequest = new BatchGetRowRequest();
        // BatchGetRow allows you to read data from multiple tables. A single multiRowQueryCriteria specifies a query condition for one table. You can add multiple multiRowQueryCriteria conditions to read data from multiple tables. 
        batchGetRowRequest.addMultiRowQueryCriteria(multiRowQueryCriteria);
    
        BatchGetRowResponse batchGetRowResponse = client.batchGetRow(batchGetRowRequest);
    
        System.out.println("Whether all operations are successful:" + batchGetRowResponse.isAllSucceed());
        if (!batchGetRowResponse.isAllSucceed()) {
            for (BatchGetRowResponse.RowResult rowResult : batchGetRowResponse.getFailedRows()) {
                System.out.println("Failed rows:" + batchGetRowRequest.getPrimaryKey(rowResult.getTableName(), rowResult.getIndex()));
                System.out.println("Cause of failures:" + rowResult.getError());
            }
    
            /**
             * You can use the createRequestForRetry method to construct another request to retry the operations on failed rows. Only the retry request is constructed here. 
             * We recommend that you use the custom retry policy in Tablestore SDKs as the retry method. This feature allows you to retry failed rows after batch operations. After you set the retry policy, you do not need to add retry code when you call the operation. 
             */
                    BatchGetRowRequest retryRequest = batchGetRowRequest.createRequestForRetry(batchGetRowResponse.getFailedRows());
        }
    }
                

    For the detailed sample code, visit BatchGetRow@GitHub.

GetRange

You can call this operation to read data in a forward and backward direction within a specified range.

You can also limit the number of rows that you want to read. If the range is large and the number of scanned rows or the volume of data exceeds the upper limit, the scan stops, and the read rows and the next primary key information are returned. You can initiate a request to start from where the last operation left off and read the remaining rows based on the next primary key information returned by the previous operation.

Note In Tablestore tables, all rows are sorted by primary keys. The primary keys of a table sequentially consists of all primary key columns. Therefore, do not assume that the rows are sorted based on a specific primary key column.
The GetRange operation may stop and return data in the following situations:
  • The amount of read data reaches 4 MB.
  • The number of read rows reaches 5,000.
  • The number of returned rows reaches the upper limit.
  • The read throughput is insufficient to read the next row of data because all reserved read throughput is consumed.
  • Parameters
    Parameter Description
    tableName The name of the table.
    direction The direction for reading.
    • If this parameter is set to FORWARD, the value of the start primary key must be less than that of the end primary key. The returned rows are sorted in ascending order based on their primary key values.
    • If the value is BACKWARD, the value of the start primary key must be greater than that of the end primary key. The returned rows are sorted in descending order based on their primary key values.

    For example, a table has two primary keys A and B where the value of A is less than that of B. If this parameter is set to FORWARD, the rows whose primary key values are greater than or equal to the value of A and smaller than the value of B are returned in ascending order from A to B. If this parameter is set to BACKWARD, the rows whose primary key values are equal to or greater than the value of B and less than the value of A are returned in descending order from B to A.

    inclusiveStartPrimaryKey The start primary key and end primary key of the range to read. The start and end primary keys must be valid ones or virtual points composed of the INF_MIN and INF_MAX type data. The number of columns for each virtual point must be the same as that of each primary key.

    INF_MIN indicates an infinitely small value. All other values of other types are greater than INF_MIN. INF_MAX indicates an infinitely large value. All other values of other types are less than INF_MAX.

    • inclusiveStartPrimaryKey indicates the start primary key. If a row that contains the start primary key exists, the row is contained in the response.
    • exclusiveEndPrimaryKey indicates the end primary key. The row that corresponds to the end primary key is not contained in the response regardless of whether the row exists.

    Rows in a table are sorted in ascending order based on the primary key values. The range to read is a left-closed and right-open interval. If data is read in the forward direction, rows whose primary key values are greater than or equal to the start primary key value and less than the end primary key value are returned.

    exclusiveEndPrimaryKey
    limit The maximum number of rows that can be returned. The value of this parameter must be greater than 0.

    An operation stops when the specified maximum number of rows is returned in the forward or backward direction, even if a part of rows within the specified range are not returned. You can use the value of nextStartPrimaryKey returned in the response to read data in the next read request.

    columnsToGet The names of the columns to be read. You can specify the names of primary key columns and attribute columns.

    If you do not specify this parameter, rows that contain all columns are returned.

    Note
    • By default, Tablestore returns the data from all columns of the row when you query a row. You can use the columnsToGet parameter to read data from specified columns. If col0 and col1 are added to columnsToGet, only the values of the col0 and col1 columns are returned.
    • If a row is within the specified range to be read based on the primary key value but does not contain the specified columns to return, the row is not contained in the response.
    • If columnsToGet and filter are used at the same time, the system first obtains the columns specified by columnsToGet and then filters the returned columns.
    maxVersions The maximum number of versions to be read.
    Note You must set at least one of the following parameters: maxVersions and timeRange.
    • If only maxVersions is specified, data of the specified number of versions is returned starting from the latest version.
    • If only timeRange is specified, all data whose versions are within the specified time range or data of the specified version is returned.
    • If both maxVersions and timeRange are specified, data of the specified number of versions within the time range is returned starting from the latest version.
    timeRange The range of versions or a version to be read. For more information, see TimeRange.
    Note You must set at least one of the following parameters: maxVersions and timeRange.
    • If only maxVersions is specified, data of the specified number of versions is returned starting from the latest version.
    • If only timeRange is specified, all data whose versions are within the specified time range or data of the specified version is returned.
    • If both maxVersions and timeRange are specified, data of the specified number of versions within the time range is returned starting from the latest version.
    • To query data within a range, you must set start and end. start indicates the start timestamp. end indicates the end timestamp. The specified range is a left-closed and right-open interval, which includes the start value and excludes the end value.
    • To query data of a specified version, you must set timestamp. timestamp indicates a specified timestamp.

    You need only to set one of timestamp and [start, end).

    The minimum value for timestamp is 0, and the maximum value is Long.MAX_VALUE. Unit: milliseconds.

    filter The filter used to filter the query results on the server of Tablestore. Only rows that meet the filter conditions are returned. For more information, see Configure a filter.
    Note If you configure columnsToGet and filter at the same time, Tablestore first queries the columns specified by columnsToGet, and then returns rows that meet the filter conditions.
    nextStartPrimaryKey The start primary key of the next read. The value of nextStartPrimaryKey can be used to determine whether all data is read.
    • If the value of nextStartPrimaryKey is not empty in the response, the nextStartPrimaryKey value can be used as the value of the start primary key for the next GetRange operation.
    • If the value of nextStartPrimaryKey is empty in the response, all data within the range is returned.
  • Example 1
    The following code provides an example on how to read data within a specified range in the forward direction. If the nextStartPrimaryKey value is null in the response, all data in the specified range is read.
    private static void getRange(SyncClient client, String startPkValue, String endPkValue) {
        RangeRowQueryCriteria rangeRowQueryCriteria = new RangeRowQueryCriteria(TABLE_NAME);
    
        // Specify the start primary key. 
        PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
        primaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME, PrimaryKeyValue.fromString(startPkValue));
        rangeRowQueryCriteria.setInclusiveStartPrimaryKey(primaryKeyBuilder.build());
    
        // Specify the end primary key. 
        primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
        primaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME, PrimaryKeyValue.fromString(endPkValue));
        rangeRowQueryCriteria.setExclusiveEndPrimaryKey(primaryKeyBuilder.build());
    
        rangeRowQueryCriteria.setMaxVersions(1);
    
        System.out.println("GetRange result:");
        while (true) {
            GetRangeResponse getRangeResponse = client.getRange(new GetRangeRequest(rangeRowQueryCriteria));
            for (Row row : getRangeResponse.getRows()) {
                System.out.println(row);
            }
    
            // If the NextStartPrimaryKey value is not null, continue the read operation. 
            if (getRangeResponse.getNextStartPrimaryKey() != null) {
                rangeRowQueryCriteria.setInclusiveStartPrimaryKey(getRangeResponse.getNextStartPrimaryKey());
            } else {
                break;
            }
        }
    }         
  • Example 2

    The following code provides an example on how to read data in the forward direction within the range determined by the value of the first primary key column. The value of the second primary key column in the start primary key is INF_MIN. The value of the second primary key column in the end primary key is INF_MAX. If the nextStartPrimaryKey value is null in the response, all data in the specified range is read.

    private static void getRange(SyncClient client, String startPkValue, String endPkValue) {
        RangeRowQueryCriteria rangeRowQueryCriteria = new RangeRowQueryCriteria(TABLE_NAME);
        // Specify the start primary key to. In this example, two primary keys are used. 
        PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
        primaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME1, PrimaryKeyValue.fromString(startPkValue)); // Set the value of the first primary key column to a specified value. 
        primaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME2, PrimaryKeyValue.INF_MIN); // Set the value of the second primary key column to an infinitely small value. 
        rangeRowQueryCriteria.setInclusiveStartPrimaryKey(primaryKeyBuilder.build());
    
        // Specify the end primary key. 
        primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
        primaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME1, PrimaryKeyValue.fromString(endPkValue)); // Set the value of the first primary key column to a specified value. 
        primaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME2, PrimaryKeyValue.INF_MAX); // Set the value of the second primary key column to an infinitely great value. 
        rangeRowQueryCriteria.setExclusiveEndPrimaryKey(primaryKeyBuilder.build());
    
        rangeRowQueryCriteria.setMaxVersions(1);
    
        System.out.println("GetRange result:");
        while (true) {
            GetRangeResponse getRangeResponse = client.getRange(new GetRangeRequest(rangeRowQueryCriteria));
            for (Row row : getRangeResponse.getRows()) {
                System.out.println(row);
            }
    
            // If the nextStartPrimaryKey value is not null, continue to read data. 
            if (getRangeResponse.getNextStartPrimaryKey() != null) {
                rangeRowQueryCriteria.setInclusiveStartPrimaryKey(getRangeResponse.getNextStartPrimaryKey());
            } else {
                break;
            }
        }
    }         
  • Example 3

    The following code is an example on how to read the data in the Col1 column within the primary key range [pk:aaa.xxx.com to pk:zzz.xxx.com) and filter the data in that column.

    private static void getRange(SyncClient client) {
        RangeRowQueryCriteria criteria = new RangeRowQueryCriteria(TABLE_NAME);
     
        // Set the primary key range to [p k:aaa.xxx.com, p k:zzz.xxx.com), and the read range is in a left-closed and right-open interval. 
        PrimaryKey pk0 = PrimaryKeyBuilder.createPrimaryKeyBuilder()
            .addPrimaryKeyColumn("pk", PrimaryKeyValue.fromString("aaa.xxx.com"))
            .build();
        PrimaryKey pk1 = PrimaryKeyBuilder.createPrimaryKeyBuilder()
            .addPrimaryKeyColumn("pk", PrimaryKeyValue.fromString("zzz.xxx.com"))
            .build();
        criteria.setInclusiveStartPrimaryKey(pk0);
        criteria.setExclusiveEndPrimaryKey(pk1);
     
        // Set the latest version to be read. 
        criteria.setMaxVersions(1);
     
        // Set the filter. The row is returned when cast<int>(regex(Col1) is greater than 100. 
        RegexRule regexRule = new RegexRule("t1:([0-9]+),", VariantType.Type.VT_INTEGER);
        SingleColumnValueRegexFilter filter =  new SingleColumnValueRegexFilter("Col1",
            regexRule,SingleColumnValueFilter.CompareOperator.GREATER_THAN,ColumnValue.fromLong(100));
        criteria.setFilter(filter);
    
        while (true) {
            GetRangeResponse resp = client.getRange(new GetRangeRequest(criteria));
            for (Row row : resp.getRows()) {
                // do something
                System.out.println(row);
            }
            if (resp.getNextStartPrimaryKey() != null) {
                criteria.setInclusiveStartPrimaryKey(resp.getNextStartPrimaryKey())
            }
        }
    }

For the detailed sample code, visit GetRange@GitHub.

createRangeIterator

You can call this operation to read data by iteration.

private static void getRangeByIterator(SyncClient client, String startPkValue, String endPkValue) {
    RangeIteratorParameter rangeIteratorParameter = new RangeIteratorParameter(TABLE_NAME);

    // Specify the start primary key. 
    PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
    primaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME, PrimaryKeyValue.fromString(startPkValue));
    rangeIteratorParameter.setInclusiveStartPrimaryKey(primaryKeyBuilder.build());

    // Specify the end primary key. 
    primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
    primaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME, PrimaryKeyValue.fromString(endPkValue));
    rangeIteratorParameter.setExclusiveEndPrimaryKey(primaryKeyBuilder.build());

    rangeIteratorParameter.setMaxVersions(1);

    Iterator<Row> iterator = client.createRangeIterator(rangeIteratorParameter);

    System.out.println("Results obtained when Iterator is used to implement GetRange:");
    while (iterator.hasNext()) {
        Row row = iterator.next();
        System.out.println(row);
    }
}           

For the detailed sample code, visit GetRangeByIterator@GitHub.