Build new applications in the cloud - or use interoperable services that run on Microsoft infrastructure to extend and enhance your existing applications. You choose what's right for you.

Table of Contents

Lab 2 — Windows Azure Storage using Table Service

 

In this lab, you will learn how to access Windows Azure Table Service by using Windows Azure SDK for Java Developers.

 

Introduction

 

This lab will focus on Windows Azure Table Service which offers structured storage in the form of tables.

 

Tables are structured tabular data stored in an Entity-Attribute-Value (EAV) Data Model; the maximum size of all attribute values of an entity is 1MB. Entities can be grouped into storage partitions, which are maintained in a single location.

 

Entity-Attribute-Value (EAV) Data Model

Entity-Attribute-Value is a data model that is used in circumstances where the number of attributes (properties, parameters) that can be used to describe a thing (an "entity" or "object") is potentially very vast, but the number that will actually apply to a given entity is relatively modest.

 

EAV models are a popular method of storing what are essentially key-value pairs, and the approach is commonly used in applications for storing global configuration information.

 

In Windows Azure Table Service, the EAV model is used as follows:

Table Data Model

The following summarizes the data model for Windows Azure Table:

The Table service does not enforce any schema for tables, so two entities in the same table may have different sets of properties. Developers may choose to enforce a schema on the client side. A table may contain any number of entities.

Entities and Properties

In Windows Azure Table, the smallest unit of data that can be stored is an entity comprising a collection of typed name-value pairs referred to as properties.

 

There are three special properties that must be present in every entity:

 

Task 0 — Prerequisites

 

Before doing this lab, if you not done so:

  1. Complete:           SDK Download
  2. Register:             Windows Azure Platform — Account Information.
  3. Get Tables Lab:  Download
 Note:

Enter Windows Azure Account Information

To use download lab sample and its JUnit, you must modify these static variables with your Windows Azure Account information:

  • public static final String AZURE_ACCOUNT_NAME = "MyAccountName";
  • public static final String AZURE_ACCOUNT_KEY = "MyAccountKey";

 

Task 1 — Access Table Storage

 

All access to Windows Azure Storage is done through a storage account. For a user to create a storage account, this is done via the Windows Azure portal web interface. The user will receive a 256-bit secret key once the account is created. This secret key is then used to authenticate user requests to the storage system.

 

A storage account is a globally unique entity within the storage system. The storage account is the parent namespace for the Table service, and is the basis for authentication. You can create any number of tables within a given storage account, as long as each table is uniquely named.

 

The namespace is used to perform all access to Windows Azure Table Service. The base URI for accessing the Table service is as follows:

Table Namespace
      http://<account>.table.core.windows.net

 

Using SDK — Create Table Storage Access

TableStorageClient::create()

 

To use Tables within Windows Azure Storage, you need a create a client that will act as a communication proxy to this service.

 

An instance of TableStorageClient creates a Table Storage Client.

 

To access this Host, you will need to replace "MyAccountName" and "MyAccountKey" with your Windows Azure Services account information.

 

Sample Code

TableSample::createStorageAccess() performs the following:

  1. Creates a TableStorageClient instance with:
    • Host Address — The Host that will service the Table Storage Client using namespace http://table.core.windows.net/
    • Azure Account Name — Used to build the URL that points to your storage.
    • Azure Account Key — Used to access your storage.
Java — Creating Table Storage Access
import org.soyatec.windowsazure.table.TableStorageClient;
import org.soyatec.windowsazure.error.StorageException;

  /* * * * */

  public static TableStorageClient createStorageAccess( String strAccountName,
                                                  String strAccountKey
                                                 )
  {
    /* * * * */
    TableStorageClient objTableStorage = null;
    /* * * * */

      objTableStorage = TableStorageClient.create(
                          URI.create( TABLE_NAMESPACE ),
                                      false,
                                      strAccountName,
                                      strAccountKey
                                      );

    /* * * * */

    return objTableStorage;
  }

 

Task 2 — Managing Tables

 

 

Create Table

Create Table operation creates a new table in the storage account.

 

 Note:

Table Names

Table names must conform to these rules:

  • May contain only alphanumeric characters.
  • May not begin with a numeric character.
  • Are case-insensitive.
  • Length must be from 3 through 63 characters long.

 

Using SDK

ITable::createTable()

 

Sample Code

TableSample::createTable()

Java — Create Table
  public static ITable createTable( TableStorageClient objTableStorage,
                                        String strTableName
                                        )
  {
    ITable objTable = null;

    try {
      objTable = objTableStorage.getTableReference(strTableName);
      if (null == objTable) {
        throw new NullPointerException(
                    String.format(  "TableStorageClient returned null ITable '%s'.",
                                    strTableName
                                    ));
      }

      if( !objTable.isTableExist() ) {
        objTable.createTable();

        if (!objTable.isTableExist()) {
          throw new Exception(
                      String.format(  "Table '%s' was not created.",
                                      strTableName
                                      ));
        }
      }
    } catch ( Exception e ) {
      e.printStackTrace();
    }

    return objTable;
  }

 

Delete Table

The Delete Table operation deletes the specified table and any data it contains.

 

A successful operation returns status code 204 (No Content).

 

When a table is successfully deleted, it is immediately marked for deletion and is no longer accessible to clients. The table is later removed from the Table service during garbage collection.

 

Note that deleting a table is likely to take at least 40 seconds to complete. If an operation is attempted against the table while it was being deleted, the service returns status code 409 (Conflict), with additional error information indicating that the table is being deleted.

 

Using SDK

ITable::deleteTable()

 

Sample Code

TableSample::deleteTable()

Java — Delete Table
  public static boolean deleteTable( ITable objTable )
  {
    boolean fSuccess = false;

    try {
      if( objTable.isTableExist() ) {
        objTable.deleteTable();

        if (objTable.isTableExist()) {
          throw new Exception(
                      String.format(  "Table '%s' was not deleted.",
                          objTable.getTableName()
                          ));
        }
      }

      fSuccess = true;
    } catch ( Exception e ) {
      e.printStackTrace();
    }

    return fSuccess;
  }

 

Sample Code— Delete All Blob Containers from Storage

TableSample::deleteTablesAll() performs the following:

  1. TableStorageClient::listTables() returns list of all table names in Table Storage.
  2. ITable::deleteTable() deletes a table and expect confirmation of deletion.

 

Java — Delete All Tables in Table Storage
  public static boolean deleteTablesAll (
                    TableStorage objTableStorage,
                    boolean fConfirmDelete
                    )
  {
    boolean fSuccess = false;

    try {
      List<String> listStrTables = objTableStorage.listTables();

      if (null != listStrTables && !listStrTables.isEmpty()) {
        for (String strTableName : listStrTables) {
          if( !TableSample.deleteTable( objTableStorage,
                                        strTableName,
                                        false
                                        ) ) {
            throw new Exception(
                        String.format(  "Failed to delete Table '%s'",
                                        strTableName
                                        ));
          }
        }

        if (fConfirmDelete) {
          /* The amount of delay for deleting container. */
          Thread.sleep(TABLE_DELETE_MSEC);

          /* Confirm all containers were deleted */
          for (String strTableName : listStrTables) {
            ITable objTable = objTableStorage.getTableReference(strTableName);
            if (objTable.isTableExist()) {
              throw new Exception ( String.format(  "Table '%s' was not deleted.",
                                                    strTableName
                                                    ) );
            }
          }
        }
      }

      fSuccess = true;
    } catch ( Exception e ) {
      e.printStackTrace();
    }

    return fSuccess;
  }

 

 

Task 3 — Enumerating Tables

 

In Table Service API, the Query Tables operation returns a list of tables under the specified account.

 

Using SDK — List Tables

TableStorageClient::listTables()

 

Sample Code

Java — List All Tables in Table Storage
  public static boolean listTables ( TableStorageClient objTableStorage )
  {
    boolean fSuccess = false;
    try {
      List<String> listStrTables = objTableStorage.listTables();

      System.out.println("------------------");
      if (null != listStrTables && !listStrTables.isEmpty()) {
        System.out.println(  "List of Tables in Table Storage\n" );

        for (String strTableName : listStrTables) {
            System.out.println(strTableName);
        }
      } else {
        System.out.println(  "No Tables in Table Storage\n" );
      }
      System.out.println("------------------");

      fSuccess = true;
    } catch ( Exception e ) {
      e.printStackTrace();
    }

    return fSuccess;
  }

 

Sample Code — Count Tables

Java — Count of All Tables in Table Storage
  public static int countTables( TableStorageClient objTableStorage )
    throws Exception
  {
    int countTables = 0 ;

    try {
      List<String> listStrTables = objTableStorage.listTables();
      countTables = listStrTables.size();
    } catch ( Exception e ) {
      e.printStackTrace();
      throw e;
    }

    return countTables;
  }

 

 

Task 4 — Insert Entity

 

The Insert Entity operation inserts a new entity with a unique primary key, formed from the combination of the PartitionKey and the RowKey.

 

 Note:

PartitionKey and RowKey Values

  • Must be string values.
  • Each may be up to 64 KB in size.
  • If you are using an integer value for the key value, you should convert the integer to a fixed-width string, because they are canonically sorted. For example, you should convert the value 1 to 0000001 to ensure proper sorting.

 

Using SDK

TableServiceContext::insertEntity(ITableServiceEntity) insert the entity into provided table. Then you can load the entity within table by loadEntity(T entity) method.

 

Sample Code

The follow is the model of entity class, it extends AbstractTableServiceEntity class that with properties: Guid uniqueId & String name.

Java — TableStorageEntity Implementation
public class TableEntitySample extends AbstractTableServiceEntity {

  protected TableEntitySample(String partitionKey, String rowKey) {
    super(partitionKey, rowKey);

    this.m_ID = new Guid();
  }

  private Guid m_ID;
  public Guid getID() { return this.m_ID; }

  private String m_strName;
  public void setName(String strName) { m_strName = strName; }
  public String getName() { return m_strName; }

  public String toString() {
    return String.format( "Guid '%s', Time '%s', Name '%s'",
                          this.getID().toString(),
                          this.timestamp.toString(),
                          this.getName()
                          );
  }
}

 

 

Task 5 — Delete Entity

 

The Delete Entity operation deletes an existing entity in a table.

 

Using SDK

TableServiceContext::deleteEntity(ITableServiceEntity)

Sample Code

Java — Delete Entity
  public static boolean deleteEntity (  ITable objTable,
                                        ITableServiceEntity objEntity
                                        )
  {
    boolean fSuccess = false;
    try {
      objTable.getTableServiceContext().deleteEntity(objEntity);
      fSuccess = true;
    } catch ( StorageException e ) {
      e.printStackTrace();
    } catch ( Exception e ) {
      e.printStackTrace();
    }
    return fSuccess;
  }

 

 

Task 6 — Query Entities

 

The Query Entities operation queries entities in a table.

 

Using SDK — Query Entities

TableServiceContext::retrieveEntities(java.lang.Class modelClass) method performs a query against a table and returns a list of all entities within that table.

Using SDK — Query Entities using PartitionKey and RowKey

TableServiceContext::retrieveEntitiesByKey(String partitionKey, String rowKey, java.lang.Class modelClass) method filter the entities whose PartitionKey and RowKey properties are all equals with the given.

 

Sample Code

Java — Query Entities using Keys
  public static boolean queryEntitiesByKeys ( ITable objTable,
                                        List<ITableServiceEntity> listEntity,
                                        java.lang.String partitionKey,
                                        java.lang.String rowKey
                                        )
  {
    boolean fSuccess = false;
    List<ITableStorageEntity> listEntityTmp = new ArrayList<ITableServiceEntity>();;

    try {
      if ( null == partitionKey || partitionKey.isEmpty()) {
        /* Return list of all Entities in Table */
        listEntityTmp = objTable.getTableServiceContext().retrieveEntities(TableEntitySample.class  );
      } else if ( null != partitionKey && !partitionKey.isEmpty()
                  && (null == rowKey || rowKey.isEmpty())) {
        listEntityTmp = objTable.getTableServiceContext().retrieveEntitiesByKey(partitionKey,  null, TableEntitySample.class );
      } else if ( null != partitionKey && !partitionKey.isEmpty()
                  && null != rowKey && !rowKey.isEmpty()) {
        listEntityTmp = objTable.getTableServiceContext().retrieveEntitiesByKey(partitionKey, rowKey ,TableEntitySample.class );
      } else {
        throw new Exception(
          String.format("Unexpected condition: partitionKey '%s', rowKey '%s'",
                    partitionKey,
                    rowKey));
      }

      if (null != listEntityTmp) {
        listEntity.addAll(listEntityTmp);
      }
      fSuccess = true;
    } catch ( StorageException e ) {
      e.printStackTrace();
    } catch ( Exception e ) {
      e.printStackTrace();
    }
    return fSuccess;
  }

Using SDK — Query Entities using Query Expressions

TableServiceContext::retrieveEntities(String queryExpression, Class modelClass) method queries entities in a table. It returns a list of entities conforming to the criteria specified in the query. If the queryExpression is not given, all rows are return.

 

The table service support top query. The following is the top query sample, it returns top 3 entities in the table.

Sample Code

Java — Query Entities using Query Expressions
    public void queryTop(){
        ...
        List apples = table.getTableServiceContext().retrieveEntities(Query.select().top(3).toAzureQuery() ,TableEntitySample.class );
        ...
    }

 

Besides, the table service support filter query, it will returns entities that satisfy the specified filter. The filter is the query language which supports the following operators:

 

Operator expression
Equal eq
GreaterThan gt
GreaterThanOrEqual ge
LessThan lt
LessThanOrEqual le
NotEqual ne
And and
Not not
Or or

 

The following characters must be encoded if they are to be used in a query string:

 

Java — Query Entities using Query Expressions
    public void queryComplexCondition() {
        ...
        try {
            List result1 = table.getTableServiceContext().retrieveEntities(Query
                .select().ge("weight", 100).toAzureQuery(),TableEntitySample.class  );

            result2 = table.getTableServiceContext().retrieveEntities(Query.select().ge("weight", 100)
                .eq("color", "red").toAzureQuery(),TableEntitySample.class);

            result3 = table.getTableServiceContext().retrieveEntities(Query.and(
                Query.select().ge("weight", 100),
                Query.or(Query.select().eq("color", "blue"), Query.select()
                    .eq("color", "green"))).toAzureQuery(),TableEntitySample.class);
        } catch (Exception e) {

        }
    }

 

 

Task 8 — Update Entity

 

The Update Entity operation replaces an existing entity with the same PartitionKey and RowKey.

 

Using SDK

TableServiceContext::updateEntity(ITableServiceEntity obj) method is used to updates an existing entity within a table by replacing it, this method does not make a call to the table service.

 

An entity's ETag provides default optimistic concurrency for update operations. The ETag value is opaque and should not be read or relied upon. Before an update operation occurs, the Table service verifies that the entity's current ETag value is identical to the ETag value included with the update request. If the values are identical, the Table service determines that the entity has not been modified since it was retrieved, and the update operation proceeds.

 

TableServiceContext::updateEntityIfNotModified(ITableServiceEntity obj) updates table entity if the entity is not modified after it is loaded from azure table storage.

 

 

Task 9 — Entity Group Transaction

 

 

Group transaction is commit multiple operations like insert entity, update entity, merge entity and delete entity within a single transaction.

 

An entity group transaction must meet the following requirements:

 

The Table service supports only a single change set within a batch. The change set can include multiple insert, update, and delete operations.

 

A batch may include a single query operation that retrieves a single entity.

 

Using SDK

IBatchExecutor ::executeBatch() method performs entity group transaction.

 

Sample Code

The following example shows the batch include insert, update and delete operations and then query the first entity.

 

Java — Entity Group Transaction
    public void batchInsertUpdateDelete() {
       TableServiceContext context = table.getTableServiceContext();
        context.setModelClass(SampleEntity.class);
        //createSampleEntities
        //sampleEntity1
        //sampleEntity2
        //sampleEntity3
        ...
        context.insertEntity(sampleEntity1);
        context.insertEntity(sampleEntity3);

        try {
            context.startBatch();
            context.insertEntity(sampleEntity2);
            sampleEntity1.name = "I am a new name";
            context.updateEntity(sampleEntity1);
            context.deleteEntity(sampleEntity3);
            context.executeBatch();
            ...
            List entities = context.retrieveEntities( null  );
            SampleEntity entity = (SampleEntity) entities.get(0);
        } catch (Exception e) {
        }
    }

 

Summary

 

In this lab, you have learned how to...

 

 

Good job!! You are done with this Lab.