4.4 Concepts - The CloudTran ORM
Developers use the ORM layer to interact with the data part of CloudTran; services and event processors can be modelled to create the processing side of the application.
From the application developer's point of view, the ORM occupies the same slot as Hibernate and JPA.
However, because of the difference in the underlying subsystems, the implementation of the ORM in CloudTran is quite different and some concepts are different.
The ORM sits across the 'The Client View' and the 'The Space/Node View'
It converts client view of the data to the space node view and vice-versa.
The ORM has 2 main interfaces which are described in the BaseORM from which all client side entities inherit and the IInternalORM iwhich is an helper interface to a Cohort space.
The BaseORM sits on the client side of the application and is responible for calling the various CloudTrans components and decomposing & converting the entity tree.
The InternalORM handles the interaction to a Cohort instance, enroling the appropriate spaces actions in a local transaction on that space.
There is no compulsion to use the ORM but is does make life easier. If the developer wants to make use of the transactional capabilities of CloudTran but doesn't wish to use
the ORM then they will have to handle to interactions between the various components in CloudTran, in short
Get a proxy to the Txb
Start a new distributed transaction
Get a proxy to each of the relevant cohort partitions
Convert any Client side entities to Space side entities
Enrol the relevant entities in a local transaction on the appropriate cohort.
Commit the local transaction,
Handle any error -aborting the local transaction and distributed transaction
Commit the distributed transaction.
Another reason for using the ORM is that the Client View entities and the Space View entities are all generated. Their generation is based on the model. Any changes in the model
are rolled into the and In addition
4.4.1 Client View
|
The data in the Client View is held as Java Objects. Each entity held in its own object (called <entityName>.java). The relationships between
entities are held as pointers to other Java Object. In actual fact these pointers are held in wrapping objects which capture the relationship and the relationship state.
For example in the Warehouse tutorial the CustomerOrder has a one-to-one relationship with a Customer and a one-to-many relationship with a CustomerOrderLine.
The to-one relationship is held in the CustomerOrder as follows:
private LinkWrapper<Customer> coCust = new LinkWrapper<Customer>( false );
The LinkWrapper contains two fields - the relationship itself and a status of the relationship, which is an 'enum'
public class LinkWrapper<E> implements ILinkable<E>, java.io.Serializable {
private E obj;
private LinkStatus status = LinkStatus.UNCHANGED;
.....
}
The enum values for the LinkStatus are:
The to-many relationship is held in the CustomerOrder as an ArrayListWrapper.
private ArrayListWrapper<CustomerOrderLine> coCol =
new ArrayListWrapper<CustomerOrderLine>( false );
The ArrayListWrapper is an ArrayList but it also contains a HashMap of LinkStatus.
public class ArrayListWrapper<E extends IEntity> extends java.util.ArrayList<E> {
HashMap<E, ILinkable.LinkStatus> status = new HashMap<E, ILinkable.LinkStatus>();
public boolean add( E o ) {
.....
}
public boolean remove( E o ) {
.....
}
....
}
In a similar way the Customer.java has an ArrayListWrapper containing the list of CustomerOrders and the CustomerOrderLine.java has a LinkWrapper hold the reference to
the CustomerOrder.
The Client View entities are generated from the model. Although they will be regenerated it is possible for the developer to add their own additional code into the
These entities are generated into the 'impl' source tree of the <Application>_CtFramework. For example
In each Client View entity there is a block of code at the bottom of the file where the developer can add their own additional code which will be preserved between regenerations.
/**************************************************************
** Additional Code
**************************************************************/
/* class uid:943df0fd-7d8a-4d41-5_Customer: .....
// put additional code here
/* class end:943df0fd-7d8a-4d41-5_Customer: .....
4.4.2 Space Object View
|
The view of the entity when it is in the Space is much akin to the way the data is held in a relational database. Each entity (called Data.java) is still held as a
Java Object, but the relationships are held as foreign keys. So in the example used in the previous section the CustomerOrderData.java contains the following
public class CustomerOrderData implements ICloudTranPersistable,
private Long coCustFK;
public Long getCoCustFK()
{
return coCustFK;
}
public void setCoCustFK( Long coCustFK )
{
this.coCustFK = coCustFK;
}
.....
}
The CustomerData.java object holds no reference to the CustomerOrder, which in turn holds no reference to the CustomerOrderLine. The CustomerOrderLine.java does hold a reference
to the CustomerOrder.
Space View entity objects are generated from the model. Any changes the user makes to the Java classes will be overwritten when the application is regenerated.
4.4.3 Implicit Transactionality
|
On of the aims of the ORM is provide a simple interface to allowing the user to deal with Java objects without having to worry about handling the transactionality.
Implicit Transactionality is the part of the ORM that handles the transactionality automatically. Each action - save, delete, updata etc. - is automatically wrapped up in
a CloudTran transaction.
Each Client View entity has the ability to persist itself. All Client View entities inherit from com.cloudtran.orm.client.BaseORM. The BaseORM is an abstract class that implements
a number of helper methods
save()
delete()
readById( Object pk )
find( IEntity template )
Each of these methods starts a distributed transaction, by calling to the TxB.
public Object save() throws TransactionExceptionRetriable
{
Object _ret = null;
DistributedTxAttributes distTx = new DistributedTxAttributes();
.....
try
{
// 1. create a new transaction
distTx = txbProxy.start( distTx );
break;
}
catch( Exceptions e )
{
.....
}
.....
}
Having obtained a transaction the BaseORM calls to the save( DistributedTxAttributes tx ) method, which is implememented in the Client View Entity. The implementation will vary from entity to
entity, but in essence decomposes the entity tree, converting the Client View into a Space View and then calls the InternalORM of all the Cohorts involved in the transaction.
This implementation is generated - the developer doesn't need to provide this implementation.
try
{
// 2. persist the object using the created distTx
_ret = save( distTx );
}
catch( Exception e )
{
.....
txbProxy.abort( distTx );
}
Once this call is completed the BaseORM then commits the distributed transaction. The BaseOrm also handles any errors, aborting the transaction where necessary.
try
{
// 3. commit the transaction
txbProxy.commit( distTx );
}
catch( Exception e )
{
.....
txbProxy.abort( distTx );
}
return _ret;
}
So, the BaseORM handles the transaction and the Client View Entity handles the conversion to the Space View. But the developer need not concern themselves with underlying code
they simply need to call the save() method on the Client View Entity. For example
Warehouse wh = new Warehouse();
wh.setAddress( "An Address" );
wh.setPostCode( "A Postcode" );
wh.setPhone( "123456789" );
wh.save();
4.4.4 Explicit Transactionality
|
Whilst the Implicit Transactionality is very useful for simple transactions there are times when the transaction can be more complicated. For instance when a number of actions need
to be performed under a single transaction
processPayment( float cost )
{
float balance = debitPayerAccount( cost );
if( balance < 0 )
{
sendOverdraftLetter();
}
creditPayeeAccount( cost );
}
In this case all these steps need to be in the same transaction. They must all succeed or all fail. To ensure this occurs they would all need to be involved in a distributed
tansaction.
processPayment( float cost )
{
DistributedTxAttributes distTx = createTx()
float balance = debitPayerAccount( cost, distTx );
if( balance < 0 )
{
sendOverdraftLetter( distTx );
}
creditPayeeAccount( cost, distTx );
}
So in these cases the developer or user needs to create and commit the transaction. Essentially it needs to mimic the code in the BaseORM. The distributed transaction can be
passed around as required including to services.
4.4.5 Internal ORM
|
Every Cohort runs the InternalORM. The job of the InternalORM is to process a set of Space Objects. These dataObjects are the Space View objects converted from the Client View objects.
Below is the InternalORM for the Save() method.
The enrol method on the Cohort gets a transactional proxy, which means that any space action performed with this proxy will performed under a local transaction.
public void save( DistributedTxAttributes distTxAttr,
IDataRow[] dataRows,
@Routing Object routing )
throws TransactionExceptionRetriable
{
CohortSubTxProxy cstp;
// 1. get the local cohort proxy
try
{
cstp = Cohort.enrol( distTxAttr, CTUtils.txbsName );
.....
}
catch( Exception e )
{
.....
}
Next the objects are either deleted from the space or written into the space - remember this is under the local transaction.
// 2. write or take the various objects get the local cohort proxy
try
{
for ( IDataRow dataRow: dataRows )
{
// This may all be put into a common area in the future
SaveAction action = dataRow.getSaveAction();
if ( action == SaveAction.DELETED )
{
delete( distTxAttr, cstp, dataRow.getData() );
}
else if ( action == SaveAction.UPDATED ||
action == SaveAction.ADDED )
{
cstp.write( dataRow.getData(),
Lease.FOREVER,
cstp.getTimeout(),
UpdateModifiers.UPDATE_OR_WRITE );
}
}
}
catch( Exception e )
{
Cohort.abort( distTxAttr );
throw e;
}
Finally the Cohort commit is called which will commit the space actions in the local space - the local transaction is committed. Note: This is not the distributed transaction.
It is only the local transaction that is committed. The local transaction will need to be committed on every Cohort used in the distributed transaction.
// 3. Commit the local transaction
try
{
Cohort.commit( distTxAttr );
}
catch( Exception e )
{
Cohort.abort( distTxAttr );
throw e;
}
.....
}
If the developer wants to perform non standard space actions they will need to mimic the flow in InternalORM
- Get the local cohort proxy
- Perform the space action
- Commit the local transaction
|