Working With Persistent Data in AIR
Why care about caching data on the client? There are 3 types of apps out there: Thin Client (HTML/JavaScript to web browser), Thick Client (Word, Excel, etc), Smart Client (RIA, a hybrid between the thick and the thin). Thin client and smart client both allow ease of deployment, but offline capability and access to hardware resources is problematic for the thin client.
AIR allows web technology to be deployed directly to the desktop. Flex, flash, PDF, HTML, JavaScript, CSS, Ajax. Always starts with a root document (HTML or SWF). HTML may contain PDF or SWF. Both have renderer, VM, and DOM. Code between the VMs can interoperate. All can be display in native OS chrome. And you can persist data locally (hence this preso!).
Storage models: Local shared objects, encrypted local store, file system, embedded SQL database.
Local shared objects. There are in package flash.net.*. Used to serialize memory resident data structures. Runs in synchronous mode. This is a monolithic storage model. So it is fine for writing huge amounts of data, but when going to retrieve a single entry out of thousands, can take a proportionately long time.
Encrypted local store. In package flash.filesystem.*. Used to store sensitive data. Runs in synchronous mode also. All data is serialized using ByteArray. So it’s like blob storage with keys. A ByteArray is like an in-memory stream of information. So you take an array, put it in a ByteArray, then add the ByteArray to the encrypted local store. This encrypted local store is just this space that is available to you based on the application and user. And it is faster at accessing the record you want. But the point isn’t that it’s faster at accessing records; the point is that it is doing security to make sure no one can read it without authorization.
File system. In package flash.filesystem.*. Provides random access to file system data. Can be asynchronous or synchronous. Two main components: File and FileStream. File represents a path to a particular file or directory. Completely OS-independent. FileStream does the actual work. Handles binary, object, text data.
Path handling: nativePath(), canonicalize(), resolvePath().
Cataloging: userDirectory(), desktopDirectory(), documentsDirectory(), applicationResourceDirectory(), applicationStorageDirectory(). File info (size, createDate, etc). copyTo(), moveTo(), etc. upload(), download(), send(), etc. browse(), browseForDirectory(), etc.
FileStream implements the IDataInput/IDataOutput APIs. Can put binary data in — readBytes(), writeBytes(), readInt(), writeInt(), readDouble, writeDouble, etc. Can do AMF3/AMF0 object serialization — readObject(), writeObject().
Embedded SQL Database. Self-contained factor is nice. You have no external dependencies; it will work the same on any OS. Each database is stored completely within a single file. Zero setup. No configuration or administration required. It just starts working. No server process required, no need for an administrator to create the database and user accounts. Handles transactions! Has a large capacity: Theoretical limit of over 2TB.
Package is in flash.data.*. Can be asynchronous and synchronous. Uses SQLConnection and SQLStatement.
SQLConnection. Establishes connection state, configuration, transactions, schema access. In async mode, also creates its own background thread. So you can create five SQLConnections to run five database operations simultaneously.
SQLStatement. Does CRUD operations. Does parameters, paging, and custom result row data types.
The database functionality is VERY fast. Can work with thousands–millions–of records in less than 1 second. Data reads are even faster.
SQLConnection: open(), attach(). The attach() will allow you to run queries on tables across multiple databases. Comes in handy during certain circumstances. Handled not too unlike SQL Server, when you are referencing an external database in your SQL statements.
SQLStatement.getResult() — Returns a SQLResult object.
SQLResult: data() returns an array of objects that contain each row of the result. complete() indicates if you pulled the whole result set. lastInsertRowID() is self-explanatory. rowsAffected() is also self-explanatory.
Note! Putting data on disk via the database can be slow sometimes. Recognize that it is a 6-step process: (1) Acquire shared lock on the database. (2) Acquire a RESERVED lock on the database. (3) In-memory rollback journal is updated. (4) Contents of rollback journal are physically written to disk. (5) Acquire an EXCLUSIVE lock to the database. (6) Write all modifications for the DB to the disk.
At this rate, you’re probably only going to get 5-6 database transactions per second. Maybe as many as 20 on a high-end box. So the key is to put MANY database operations all in a single transaction.
Transactions. SQLConnection manages the transaction. Use SQLConnection.begin() to start the transaction. Then, commit() and rollback() work as expected.
Storage classes. There are numbers (int/real), text, and blobs.
Affinity. Declaring a data type on a column determines the column’s affinity. You’re saying, “I want this field to be stored like this.” However, if you send a string to a field that should be a number, it won’t throw an error, it will just put it in there as a string. Supports ActionScript affinities of boolean, date, int. Next beta should support XML, XMLList, Object, etc. But currently, you can put arrays or objects in with blob, which will then treat it like a ByteArray.
Introspection. Access to the table/view, column, index, and trigger information, including the SQL used to create the entity. But you can do selective loading, i.e. I want to see the tables in this database. Just use SQLConnection.loadSchema(). Will return a SQLSchemaResult that has tables, views, triggers, and indexes. These are all arrays of objects with the associated values.

December 6th, 2007 at 5:17 pm
good breakdown. thanks!
December 25th, 2007 at 7:26 pm
Great article and thanks for it.
June 3rd, 2008 at 7:07 am
Interesting… that’s for sure. Very interesting. I like what you have to say about ‘thin client’ and ‘thick client.’ To be honest, I’ve never really experienced much advantage from thin client on its own – well, not for most applications anyway. So, the idea of ’smart client’ – a smart inbetween – is definitely called for and, I’ll be honest, I’m very excited about what’s happening with this and the future that may come through AIR and similar concepts. I’m definitely keeping my eyes and ears open, waiting for something to come around that works within my industry and sphere.