large dataset

Pageable Row Set???

So my coworker Justin has been telling me that I should be writing a blog about some of the stuff I’ve been working on with Flex and Java. So here’s my start....

I’ve been working with decently large datasets, about 10k to 20k rows of data. The data has many attributes, so the size of it is quite large, 10M or so.

Of course I started with a nice gzip on the server, so that compressed the data down which increases the download of the dataset, but of course this is a stop gap measure because as the size of the data grows the difficulty of dealing with it grows.

I’ve looked at java side RowSet and using BlazeDS PageableRowSetCache, with a homegrown actionscript paged array collection.

I didn’t find much information to do this, so I thought I would write up what I did.

First, I did the basic java remote method.



RowSet rowSet = new WebRowSetImpl();
rowSet.setReadOnly(true);
rowSet.setDataSourceName("java:comp/env/jdbc/DATASOURCE_CONNECTION");
rowSet.setCommand("SQL STATEMENT");
rowSet.execute();

With this row set we can now call the PageableRowSetCache.cacheRowSet.

PageableRowSet pRowSet = PageableRowSetCache.cacheRowSet(rowSet, 15);

We now pass the PageableRowSet back to the Flex side. The nice thing is that the PageableRowSet gets converted into an mx.util.ObjectProxy that has the following properties that I use.
serverInfo:Object which contains
    id:String // unique
    totalCount:int
    initialData:Array
    serviceName:String

The initialData holds the first page of data and you should use the length to identify the pageSize to fetch.
The totalCount holds the complete row count of the SQL STATEMENT that was excecuted.
The serviceName holds the destination name for requesting pending data. It has a default value of PageableRowSetCache, you have to setup the JavaAdapter in the remoting-config.xml file i.e.
<destination id="PageableRowSetCache">
        <properties>
            <scope>application</scope>
            <source>flex.messaging.services.remoting.PageableRowSetCache</source>
        </properties>
</destination>


The flex.messaging.services.remoting.PageableRowSetCache has a method called 00001: getRecords(id:String, start:int, numOfRecords:int)   which is used to retrieve other data from the Row Set. The id is the unique id in the serverInfo object.

So at this point we’ve set up everything without writing much Flex code. So now comes the bigger piece of it.

public class PageableCollection extends ArrayCollection {
    public static var pendingObject:Object = "Pending";
    protected var pagedFetched:Object = new Object();
    protected var pagedObject:Object;
    protected var pageService:RemoteObject;
    protected var pageSize:Number;
    public function PageableCollection(obj:Object) {
        pagedObject = obj;
        var array:Array = new Array(pagedObject.totalCount + 1);
        var i:int;
        for (i = 0; i < pagedObject.initialData.length; i++) {
            array[i] = (convert(pagedObject.initialData[i] as Array));
        }
        pageSize = i;
        for (; i < pagedObject.totalCount; i++) {
            array[i] = pendingObject;
        }
        super(array);
        pageService = new RemoteObject(pagedObject.serviceName);
        pageService.addEventListener(ResultEvent.RESULT, handleResult, false, 1, true);
    }
    override public function get length():int {
        return pagedObject.totalCount + 1;
    }

    override public function getItemAt(index:int, prefetch:int = 0):Object {
        var obj:Object = super.getItemAt(index, prefetch);
        if (obj != pendingObject) {
            return obj;
        }
        var page:int = Math.floor(index / pageSize);
        if (pagedFetched[page] != null) {
            return obj;
        }
        pagedFetched[page] = true;
        var token:Object = pageService.getRecords(pagedObject.id, page * pageSize, pageSize);
        token.index = page * pageSize;
        return obj;
    }
    protected function handleResult(event:ResultEvent):void {
        for (var i:int = 0; i < event.result.Page.length; i++) {
            this.setItemAt(convert(event.result.Page[i]), i + event.token.index);
        }
    }
    public var  convert:Function = function(a:Object):Object{
        return a;
    };
    }
}

A few things to remember about this are
  • You need a java sticky session on the server side.
  • Sorting and filtering can be problematic, (I’m hoping to have a post to address this issue)
  • You need to manage the PageableRowSetCache, such as releasing it when you’re done

The one thing I really like about this implementation is how it allows the ArrayCollection to deal with keeping the data and this class to deal with acquiring the pending data, as well as being able to replace an ArrayCollection with a PageableCollection.


|