The Dreaded “Not Serializable” Problem
GWT turns all of your Java code into JavaScript, but it also obfuscates everything. With this in mind, it makes serialization a bit more complex than usual. GWT generates a serializer and deserializer for each class that could be transported across the wire to an RPC service. The difficulty comes in knowing which types had code generated, and which didn’t. GWT solves this problem for itself with the Serialization Policy file, where the compiler lists all of the classes that the client code will know how to deserialize.
This however leads to another problem: what happens when something unexpected gets in the way. Hibernate is the most used, and thus most complained about when it comes to Serialization problems. Hibernate turns any Collection object into a special “lazy” implementation that will only load your data when you ask for it. All very fine and well for a server bound application, but a GWT application needs all that data up front for serialization. When GWT comes to sending that List<Message> to the client, it chokes. It knows how to serialize an ArrayList, and LinkedList (since you worked with them on the client side), but a Hibernate List or PersistentCollection is a totally unknown type, so it’s not in the Serialization Policy, so the server side throws an Exception at you.
So how does EoD SQL help with these problems? Read on to find out!
Loosing the Lazy Loading
EoD SQL (unlike most OR mapping layers) makes no attempt to understand the internal relationships between you objects. The reason for this is that EoD SQL is technically dialect neutral, it doesn’t actually care what you put into you @Select annotation so long as it can create a PreparedStatement and populate it with parameters. This frees you to take maximum advantage of database extensions, or weird SQL dialects. It also means that EoD SQL will never generate any SQL code for you: what you type is what gets executed.
Within a GWT application, you don’t have a way to block until a method is complete, and thus lazy-loading data becomes a chore, since you need to work with a lot of callbacks (a nice way of abstracting lazy-like-loading is posted on my blog). This means that most of the object model given to GWT needs to be (at least partly) eager-loaded.
So because EoD SQL doesn’t do any lazy-loading, it “forces” you into thinking about how much data and what data you will be sending to the client. It takes a little getting used to, but over time you will find your code much clearer to read and running much faster (since less data comes from the database, fewer queries are run and the JavaScript has less data to deal with).
Arrays instead of Collections
The first trick to working with GWT is: when you need more than one object fetched from the database (which is most of the time), return an array instead of a Collection:
public interface MessageQuery extends BaseQuery {
/*
* This is not a great way to do things in EoD SQL 2.0 when dealing with GWT.
* This method will return an ArrayList of Message objects, however the List implementation could
* possibly be changed in the future (not likely). Further the ArrayList is populated using the basic
* List.add(Object) method. The ArrayList will re-copy it's internal array over-and-over again as
* the ResultSet is scanned.
*
* So while this method will work with GWT, it's really not recommended.
*/
@Select("SELECT * FROM messages WHERE parent_id = ?1 ORDER BY date DESC LIMIT ?3 OFFSET ?2")
List<Message> selectChildMessagesAsList(int parentId, int start, int count);
/*
* This won't work at all with GWT. The DataSet may offer lazy-loading
* and some other wonderful tricks, but since the client code has no idea
* how to deal with a DataSet it's useless here. I thought I'd put it in for completeness.
*/
@Select("SELECT * FROM messages WHERE parent_id = ?1 ORDER BY date DESC LIMIT ?3 OFFSET ?2")
DataSet<Message> selectChildMessagesAsDataSet(int parentId, int start, int count);
/*
* This is the way to do it! Using only arrays for Serialization has a side effect
* on the GWT side of things: less JavaScript code for your clients to download.
* GWT understands how to deserialize arrays, and doesn't need to include
* Serialization code for different List implementations.
*/
@Select("SELECT * FROM messages WHERE parent_id = ?1 ORDER BY date DESC LIMIT ?3 OFFSET ?2")
Message[] selectChildMessagesAsArray(int parentId, int start, int count);
}
Now for the big question: why are arrays “so much better” than a List of some sort? EoD SQL takes fairly extreme measures to keep the performance of an Array as fast as possible. For the most part (that is: on most JDBC drivers) the array will be pre-sized and fetched backwards. This sounds very strange, but it is generally a lot faster than trying to work forwards and re-growing the array whenever it runs out of space (and then down-sizing it at the end). EoD SQL will run forwards if the JDBC driver requires it, but for the most part it runs backwards.
This technique will not only improve performance, JavaScript size, and simplicity of the code (arrays are just easier in most cases); it’ll also reduce the amount of memory your server uses (since the array is pre-sized and never copied).
Finally: because the type is declared as an array, you know exactly what Serialization code GWT is going to generate.
Looking to the Future (EoD SQL 2.1)
In EoD SQL 2.0 you can declare Collection objects as return types, but only as their interfaces (as you saw in the above code example). EoD SQL 2.1 has a new feature which allows you to declare concrete implementations as return types. This is the preferred method of declaration for GWT (since again: it minimizes the amount of JavaScript generated). A few nice little examples:
public interface MessageQuery extends BaseQuery {
@Select("SELECT * FROM messages WHERE parent_id = ?1 ORDER BY date DESC LIMIT ?3 OFFSET ?2")
ArrayList<Message> selectMessages(int parentId, int start, int count);
@Select("SELECT title, date, author FROM messages WHERE parent_id IS NULL ORDER BY date")
MyCollectionImplementation<Message> selectTopLevelMessages();
}
The big difference between 2.0s handling of Collection objects, and 2.1 is that 2.1 will allow the Collection implementation (including those declared as their interfaces) to optimize their fetching of the data internally (if they are able). Thus returning a List or ArrayList will no longer cause the resize-resize-resize effect (or shouldn’t anyways). Collections will be given the same treatment as arrays in 2.1.
Conclusion
So we’ve looked at four different situations where EoD SQL works really well.
The concept to take away from this (I feel) is that: with EoD SQL you are in total control of how the code is going to behave.
If you need lazy-loading and scrolling data (like in our Swing example), you can use a DataSet; to minimize memory a DataIterator; and when lazy-loading can’t be used because of other constraints Collections and Arrays are available. Whatever the situation: EoD SQL is going to make life that little bit easier for you, without you giving up control of what goes on behind the scene.