In this lab, you will learn how to access Windows Azure Table Service by using Windows Azure SDK for Java Developers.
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 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:
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.
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:
Before doing this lab, if you not done so:
Note: |
|---|
Enter Windows Azure Account InformationTo use download lab sample and its JUnit, you must modify these static variables with your Windows Azure Account information:
|
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
| |
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.
TableSample::createStorageAccess() performs the following:
| 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;
}
| |
Create Table operation creates a new table in the storage account.
Note: |
|---|
Table NamesTable names must conform to these rules:
|
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;
}
|
|
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.
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;
}
| |
TableSample::deleteTablesAll() performs the following:
| 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;
}
|
|
In Table Service API, the Query Tables operation returns a list of tables under the specified account.
TableStorageClient::listTables()
| 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;
}
| |
| 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;
}
| |
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
|
TableServiceContext::insertEntity(ITableServiceEntity) insert the entity into provided table. Then you can load the entity within table by loadEntity(T entity) method.
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()
);
}
}
| |
The Delete Entity operation deletes an existing entity in a table.
| 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;
}
| |
The Query Entities operation queries entities in a table.
TableServiceContext::retrieveEntities(java.lang.Class modelClass) method performs a query against a table and returns a list of all entities within that table.
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.
| 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;
}
| |
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.
| 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) {
}
}
| |
The Update Entity operation replaces an existing entity with the same PartitionKey and RowKey.
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.
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.
IBatchExecutor ::executeBatch() method performs entity group transaction.
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) {
}
}
| |
In this lab, you have learned how to...