Saturday, November 28, 2009

Using ZK with an OODB such as db4o

A recent job opening I applied for asked for GWT or ZK experience.  I had played very briefly with GWT when it first appeared, but had no idea what ZK was.  So I looked at it.  It is another AJAX / Rich Internet Application framework, similar to GWT in that you still write most of your underlying code in Java.  The one big difference from GWT is that ZK is server-centric, running most of the application on the server, whereas GWT is client-centric, running most of the app on the client.  Naturally, both sides tout themselves as "better".

One of the ZK demos is a simple TODO list, where the entries get stored into a relational database.  (they use HSQLDB).  You can download the ZK demo project file here.  Look inside to get the .war file and the source code.  They define a simple java-bean Event.java, plus a couple of simple, relatively straightforward XML files.  I chose to work from their version with a separate java file & class EventController.java, instead of binding all this logic into a an XML page. EventController.java looks reasonable - there's a couple of fields required to interact with the specific GUI, otherwise it's very clean..  But then you look at the DAO, EventDAO.java.  It's big.

The "problem" is in the persistence layer, binding the TodoEvents to the database.  Now, there's nothing wrong with EventDAO.java.  It's just a real shame that for such a nice, lightweight, concise framework like ZK, the DAO code requires over 150 lines of boilerplate Java code to handle the JDBC calls.  For a single object.  For a more "real" app, with a few kinds of persistent objects, this code would grow accordingly.  At some point, one would consider using an ORM like Hibernate.  But that has it's own "activation energy" hurdle.  While reading the ZK forums, I came across a post that suggested the used of db4o.  Having used db4o a little myself, this idea really rang true.  I didn't see where anybody was doing this yet (see Jease for a project just starting that seems to be on this track).

Will using an OODB make your ZK code simpler?  Here's my first pass. And the answer is a resounding YES.  I am neither a ZK nor a db4o expert, so there are probably some improvements that can be made.

 TodoEvent.java has no change.

Their EventController is replaced with GenericController.java.  It uses generics to make it more expandable for future classes, though this may be getting too cute. Otherwise, it is similar to the original code.  It has the same slightly "kludgy" fields from the original, so as to link back to the web GUI

   private T current;
   Listbox box;


One difference is that the add() method must clone the object.  This is because OODBs (at least the two I'm using) do not differentiate between and update and an insert - they just use "store".  In technical terms, their definition of object identity differs from a RDB.  Fortunately, ZK priovides a utility class, Objects, to do most of the work.

   public void onClick$add() {     
      if (current != null) {
         current = (T) Objects.clone(current);
         getDB().store(current);
      }
   }



Just like the original EventController delegated persistence to an EventDAO, GenericController delegates to a "DAO", a simple interface IOODB.java.  I made two implementations, one for db4o, Db4oDB.java and another for NeoDatis, NeoDatisDB.java.  For this demo, the DB to use is defined in GenericController.DBHolder, but in a real life app this would be determined somewhere else.

The final java class is TodoEventController.java, which is a near trivial extension of GenericEventController.  It adds one method, getAllEvents().  This is because the index.zul file refers to "win$composer.allEvents".  ZK uses reflection to callback to the object, so we need to implement a method with a name it can find.  Obviously, one could edit the index.zul file instead.  The only change in index.zul is to refer to our new controller class:

window id="win" title="To do list" width="640px" border="normal" apply="com.flyingspaniel.zk.oodb.TodoEventController"

In conclusion, from this simple foundation, one can support most basic object types, on two different OODBs.  It's far easier than writing specific JDBC code, and less heavyweight than configuring Hibernate.  Give it a try!  And please let me know what you think and how this can be improved.  All the source code can be found at this Wikisite.

4 comments:

  1. Hi Morgan,

    just had a quick look at your implementation: I don't see any commits in your code. Without a commit you can't be sure that your updates are persisted to disk. db4o / NeoDatis are doing some kind of autocommit when you close the database, but if your app crashes you're uncomitted transactions will be lost.

    Cheers, Maik

    ReplyDelete
  2. Yes, you are right. Should I just do a commit after every call???

    ReplyDelete
  3. For your use-case I would say yes. Generally a commit should be performed after a common set of business operations which should be regarded as "atomic": either all updates are stored or none. E.g. if you're going to do "batch-updates" you should call commit only after the whole batch, otherwise performance will be not so good because of flushing changes to disk with each commit.

    ReplyDelete
  4. Maik - I updated the files and blogged about it. Thanks for the tip.

    ReplyDelete