Deep Data Synchronization with BlazeDS

Due to space constraints, you’ve been presented with the simplified fragments of the DataCollection code to highlight its main features and give you a push in the right direction, should you want to create your own version of such a collection. Here are a few more possible approaches that may prove useful.

Note

You can find the complete and up-to-date source code of the DataCollection class (900+ lines of code) in the SourceForge repository.

Nested DataCollections

Previously, you learned about data synchronization between DataCollection and remote Java objects via the method sync(). But what if you have a situation with nested DataCollection objects that can be modified on the client side? How do you synchronize the changes in this case? Here’s the magic line of code that will perform deep synchronization of the DataCollection and all its nested children:

collection.sync(true);

If you don’t like manual coding, Clear Data Builder will perform deep synchronization of hierarchical DataCollections with the server, so that if an item of the collection contains child collections (Example 6-16, shown later), the entire tree of changes gets synchronized with the Java backend in one transaction.

Consider a sample order-processing application (Figure 6-7) that allows the user to navigate from order to order, editing the master information (order) as well as its details (order items).

The order-processing application

Figure 6-7. The order-processing application

The user can modify either of the data grids. All interactive changes are accumulated in the underlying DataCollection until the button labeled Commit is clicked. That’s exactly when deep sync happens in one transaction—it’s all or nothing, the commit of all changes or complete rollback.

Each of the data grids is supported by a subclass of DataCollection: OrderCollection and OrderItemCollection, respectively (Example 6-14).

Example 6-14. OrderCollection and OrderItemCollection

package collections {
import com.farata.collections.DataCollection;
public class OrderCollection extends DataCollection {
   public function OrderCollection(source:Array=null) {
      super(source);
      destination="com.farata.test.Order";
      method="getOrders";
   }
}
}

package collections {
import com.farata.collections.DataCollection;
public class OrderItemCollection extends DataCollection {
   public function OrderItemCollection(source:Array=null) {
      super(source);
      destination="com.farata.test.Order";
      method="getOrderItems";
   }
}
}

The source code of the application shown in Figure 6-7 is listed in Example 6-15.

Example 6-15. The code of the order-processing application object

<?xml version="1.0" encoding="UTF-8"?>
<!--OrderEntryDemo.mxml -->
<mx:Application
   xmlns:mx="http://www.adobe.com/2006/mxml"
   xmlns="*" xmlns:collections="collections.*">
   <collections:OrderCollection id="orders"/>
   <mx:ControlBar>
      <mx:Button label="Fill"  click="selectedOrder=null;orders.fill()"  />
      <mx:Button label="Commit"  click="orders.sync(true)"
         enabled="{orders.commitRequired}" />
   </mx:ControlBar>
   <mx:VDividedBox  >
      <OrdersPanel id="master" orders="{orders}"
         orderSelectionChange="selectedOrder = event.order"
      />
      <OrderItemsPanel id="detail" width="100%"
         selectedOrder="{selectedOrder}"
      />
   </mx:VDividedBox>
   <mx:Script>
      <![CDATA[
         import com.farata.test.dto.OrderDTO;
         [Bindable] private var selectedOrder:OrderDTO;
      ]]>
   </mx:Script>
</mx:Application>

The example application contains two custom objects: OrdersPanel and OrderItemsPanel. The OrdersPanel object uses OrderCollection as a data provider for its data grid. Each item of the OrderCollection carries orderItems referring to the child collection of line items of this order. At the application level, you need to expose only the master collection orders, which hold the entire master/detail data hierarchy.

The Commit button is enabled automatically when there are changes to commit (the collection’s array of ChangeObjects is not empty). On click, the sync(true) is called, requesting deep synchronization, or persistence of all nested DataCollections:

<mx:Button label="Commit"  click="orders.sync(true)"
   enabled="{orders.commitRequired}" />

As mentioned earlier, you can substantially reduce the amount of manual coding in DTOs: Clear Data Builder will do it for you. In particular, it takes the Java class Order written by you (Example 6-17, shown later) and generates the ActionScript class _OrderDTO and its subclass OrderDTO (Example 6-16).

Example 6-16. A DTO with nested collection orderItems

package com.farata.test.dto{
import collections.OrderItemCollection;
import com.farata.collections.dto.HierarchicalDTOAdapter;
import com.farata.collections.dto.IHierarchicalDTO;

[RemoteClass(alias="com.farata.test.dto.OrderDTO")]
public class OrderDTO extends _OrderDTO implements IHierarchicalDTO{
[Transient] [Bindable] public var orderItems:OrderItemCollection;
[Transient] public var adapter:HierarchicalDTOAdapter;

public function OrderDTO() {
    super();
    adapter = new HierarchicalDTOAdapter(this);
    orderItems = new OrderItemCollection();
    adapter.addCollection(orderItems);
}

public function get childCollections():Array {
    return adapter.childCollections;
}

public override function set order_id(orderId:String):void {
    if (orderId !== super.order_id) {
         super.order_id = orderId;
         orderItems.fill(order_id);
    }
}
}//OrderDTO
}

Note the [Transient] metadata tags that ensure that these objects won’t be serialized and sent to the server.

Though the properties of the _OrderDTO will match the fields returned by the SQL select specified in the doclet section of getOrders() in Example 6-17, the subclass OrderDTO is your playground. You can add any code there, and it won’t be overwritten by the next CDB code generation.

In particular, the secret sauce here is that OrderDTO implements the IHierarchicalDTO interface, which you have to add manually to the generated OrderDTO if you want your collection to include nested collections. You’ll also need to add code that uses HierarchicalDTOAdapter, the getter childCollections, and the setter order_id as it’s done in the example.

Example 6-17 is the abstract Java class that is used with CDB to generate an ActionScript DTO from Example 6-16.

Example 6-17. Order.java

package com.farata.test;
import java.util.List;
/**
* @daoflex:webservice
* pool=jdbc/test
*/
public abstract class Order
{
/**
* @daoflex:sql
* sql=:: select order_id, customer_first_name,
* customer_last_name, order_date from simple_order
* ::
* transferType=OrderDTO[]
* keyColumns=order_id
* updateTable=simple_order
* autoSyncEnabled=true
*/
public abstract List getOrders();
/**
* @daoflex:sql
* sql=select * from simple_order_item WHERE ORDER_ID=:orderId
* transferType=OrderItemDTO[]
* updateTable=simple_order_item
* keyColumns=order_id,item_id,product_name
* autoSyncEnabled=true
*/
public abstract List getOrderItems(String orderId);
}

Note

CDB doesn’t force you to use SQL for the generation of ActionScript DTOs and automating the work with fill() and sync() methods. CDB allows your DataCollections to remote to any Java class implementing the com.farata.daoflex.IJavaDAO interface that returns an arbitrary Java DTO. See the CDB documentation for more details.

The autoSyncEnabled attribute in Example 6-17 comes in handy when more than one user works with the same application and the same piece of data; Clear Data Builder offers an autonotification mechanism for data modifications. Changing the value of the autoSyncEnabled attribute allows you to turn on or off the sending of such notifications. For details, see the post at http://www.tinyurl.com/autosync.

Batching Remote Calls

In Example 6-7, you saw that the sync() method performed three steps (delete, update, and insert items) to maintain the referential integrity of data changes. If you want to perform updates of more than one DataCollection in one transaction, you can batch them. In the order-processing application, you have a case of nested collections, children have to be deleted prior to parents, and parents need to be inserted prior to children. But you may have another business reason to run multiple updates as one transaction.

That’s where the BatchService class from clear.swc comes into play. It treats a sequence of several remote method calls as a batch, or simply as an array of BatchMember objects containing such elements as destination name, method name, and array of arguments.

Instead of making multiple remote calls, BatchService sends the entire batch as an argument of one remote call. On the server side, this call is performed by a Java class, com.farata.remoting.BatchGateway, located in daoflex-runtime.jar, which comes with CDB. In turn, BatchGateway’s method execute(List<BatchMember>) invokes the required remote calls sequentially, wrapping the entire sequence begin/commit/rollback as prescribed by the Java Transaction API (Figure 6-8).

Batching remote calls

Figure 6-8. Batching remote calls

The following code snippet illustrates how you can add two collections from the order-processing example to one batch and send it for processing to the server:

var bs: com.farata.remoting.BatchService;
  ...
  bs = new BatchService();
  bs.addEventListener(FaultEvent.FAULT, onFault);
  bs.registerCollection(orders, 0); //0 - default (top) priority, parent
  bs.registerCollection(orderItems,1); //1 - priority, child of "0"
  ...
  var batch:Array = bs.batchRegisteredCollections();
  bs.send(batch);

You can use the BatchService not only with DataCollections, but also with regular Flex collections. It allows you to batch the execution of any sequence of remote calls.

Users of the SQL-based branch of CDB benefit from automatic generation of the required Java functions. Otherwise, your Java DAO has to implement the interface IBatchTransactionServiceSupport.

If your transaction includes only a data collection, consider using DataCollection.sync(true), which further reduces the amount of manually written code required to perform transactional persistence of associated collections.

By now, you should have a good understanding of how to approach data automation in Flex and BlazeDS, and the next section will show you how to use the headers of the AMF messages that travel with your data in the protocol layer.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.120.206