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.
You can find the complete and up-to-date source code of the
DataCollection
class (900+ lines of
code) in the SourceForge
repository.
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 DataCollection
s 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 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 ChangeObject
s is not empty). On click, the
sync(true)
is called, requesting deep
synchronization, or persistence of all nested DataCollection
s:
<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); }
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 DataCollection
s 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.
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).
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 DataCollection
s, 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.
18.118.120.206