Introducing FOORM – Form Oriented Object Relational Mapper

FOORM Presentation and Persistance

Note: This is extracted from one of my earlier blog posts. FOORM is still a work in progress and while I love using it and think it is the best presentation and persistence framework the world has ever seen – it is still early days and the framework maker must endure using it for a *long* time before accepting any new customers. So this is and will continue to be a work in progress for quite some time with the Sakai support for LTI as my test bed for FOORM.

In FOORM you build a model using an array of strings that look as follows;

  static String[] CONTENT_MODEL = { 
      "id:key", 
      "tool_id:integer:hidden=true",
      "SITE_ID:text:maxlength=99:label=bl_content_site_id:role=admin",
      "title:text:label=bl_content_title:required=true:maxlength=255",
      "frameheight:integer:label=bl_frameheight",
      "newpage:checkbox:label=bl_newpage",
      "debug:checkbox:label=bl_debug",
      "custom:textarea:label=bl_custom:rows=5:cols=25:maxlength=1024",
      "launch:url:hidden=true:maxlength=255",
      "xmlimport:text:hidden=true:maxlength=16384", 
      "created_at:autodate",
      "updated_at:autodate" };

This is kind of like a Rails model in colon-separated form. One thing that FOORM assumes is that you will be internationalizing your UI and so it even encodes the field labels..

You can build an input form with the following lines of code:

String formInput(Object previousData, String [] modelInfo, Object resourceLoader)

This returns a set of input tags, one for each field, repopulated from the previous data (can be Properties, Map, or ResultSet) and using the provided resource loader.

You embed the form tags in some Velocity as follows:

$formInput

There are utilities to validate incoming data, and extract it into the right objects and type for inserting into a database. with a very few lines of code. Here is an extract from request properties into newMapping and insert into the database:

    HashMap newMapping = new HashMap();

    String errors = foorm.formExtract(requestProperties, formModel, 
        rb, true, newMapping, null);
    if (errors != null) return errors;

    String[] insertInfo = foorm.insertForm(newMapping);
    String makeSql = "INSERT INTO " + table + " ( " + insertInfo[0] + 
          " ) VALUES ( " + insertInfo[1] + " )";
    final Object[] fields = foorm.getInsertObjects(newMapping);
    m_sql.dbInsert(null, sql, fields);

It supports a very REST-style property bag approach to data models except that all the fields are properly and fully realized as database columns so they can be searched, selected, grouped, and ordered with as sophisticated query as you want to create.

FOORM is designed to be extended so you have the generic Foorm and you extend it to make a SakaiFoorm that captures all the Sakai-specific rules for generating fields, handling internationalization, etc.

I even dispensed with the “CREATE TABLE” scripts – since Foorm knows the entire model, it can make the tables and sequences automatically. It know about the various rules for fields in MySql, HSQL, and Oracle. The AutoDDL code looks as follows:

 String[] sqls = foorm.formSqlTable("lti_content", 
     LTIService.CONTENT_MODEL, m_sql.getVendor(), doReset);
        for (String sql : sqls)
          if (m_sql.dbWriteFailQuiet(null, sql, null)) M_log.info(sql);

I could make this more succinct with an overridable method in Foorm. Foorm still needs to learn about changes to data models and automatically generate “ALTER TABLE” commands. I will write that code when I need to do a conversion when I release a future version of DBLTIService is released and I need to expand the model.

I think that Foorm has a lot of potential. It is kind of a weird combination of a partial presentation layer and partial object relational mapper. It is focused on providing useful library utilities that let the developer get as tricky as they want, making sure that the nasty repetitive common tasks take as little effort as possible.

You can ask me “Why not Hibernate?” or “Why not Spring JDBC?” at a bar sometime. Be prepared to hear my voice raise a few notches as I rail about “Hibernate’s is an overbearing over-engineered steaming pile of…(oops did I say that out loud?)” or “Spring JDBC makes SQL portable in Java only to the level that they solve the trivial problems and punt on anything mildly interesting or complex…”. If I have had a few beers before the conversation starts, it will be more (or less) interesting.

Oh by the way, Foorm provides a nice, generic way to handle paged SQL queries. I still need to build advanced WHERE clauses, and support ORDER BY and GROUP BY operations.

This is why Foorm is still a prototype and I don’t want anyone else using it for a while. I feel that any library / framework should be used by its creator for a long time, solving lots of real-world problems before they claim to have anything truly reusable. But I figured I would show folks what I am thinking.

I have been experimenting with finding the right combination of power and convenience for both display layers and persistence. Before FOORM there was OMG-ORM (http://omg-software.com/) – it too was an exploration of the balance between powerful capabilities and intrusiveness as well as trying to make things intuitive. FOORM is much simpler than OMG and I find it more natural to use.

3 Comments

  1. Steve says:

    Why not Spring JDBC? ;)

    I don’t think Spring JDBC ever set out to make SQL portable in the way that Hibernate does. It does give you complete freedom to run whatever SQL query you want. In it’s simplest form it just takes away the drudgery of connection, transaction and result handling boilerplate code. It also allows you to quickly get that data back as Objects.

    I would still like to enjoy a beer though.

    cheers.

  2. I agree – my complaint about Spring JDBC is that it is a layer and it chooses to let you see what it wants you to see. As an example HSQLDB does auto increment differently than any other JDBC :(. But there no way to even hack it through Spring JDBC. Spring JDBC copes with nasty rules about finally clauses and helps you avoid a few subtle differences in JDBC drivers – but frankly Sakai’s Basic SQL service does the same thing and in some ways is superior because we have source code and if we want to make HSQLDB work – we can unlike Spring JDBC that would never consider adding support for the crusty old HSQL 1.8.

    Hibernate’s flaw is that it is a layer that is too thick and gets in the way of performance tuning and efficiency. Spring JDBC’s flaw is that while it is a thin layer – without source, we cannot choose how complete we want that layer.

    The lesson is that anytime there is a layer, it is a red flag as applications get more complex and have to meet a wide range of weird requirements (like use HSQL 1.8) that are not negotiable.

    Layers are not always a good thing – look at OSI versus TCP/IP – the first had a pretty logical well-throught layering by a bunch of ivory tower folks as an architecture exercise. TCP/IP had layers where they organically worked them out as part of an iterative engineering process and at times let the layers stay a little porous – and TCP/IP won.

    Computer Scientists (including myself) are trained that layers, abstraction and beautiful architecture diagrams are an unquestioned “pure good” with no downside. Real world experience for me suggests that the better pattern is one of utilities that help a programmer function more effectively rather than a non-porous layer that attempts to solve every possible problem using magic and completely isolate the developer from the bare metal.

    The problem is that neither Hibernate nor Spring are really interested in serving the programmers that use them. Both products commercially gain when they create “tribes” within the Java community that only know the “Hibernate way” or “Spring way” and cannot function without those as crutches. Once these people are properly indoctrinated they force others to learn those frameworks and the Spring or Hibernate market share and influence grows.

    You would wisely turn this around on me and suggest that I and others in Sakai created a similar lock-in situation where we forced developers to join a tribe and learn all the Sakai CLE secret handshakes and then we would pull them in so they could not escape and then they would force others to join our tribe. If you said that, I would have to agree with you. The difference between me and Spring / Hibernate is that I accept responsibility for creating an architecture that led to more lock in than necessary. The Spring or Hibernate folks want you to believe that being locked in is better than being free because they take care of so much for you – in prison you get free food three times per day and a roof over your head. At least I admit my shortcomings.

    All this is to say that Foorm is an attempt to explore a rich and powerful set of utilities that *help* a programmer be more efficient without putting a layer between them and their iron. It is of course a very rough work in progress and may never be interesting to anyone other than me :)

    My goal is a combination of efficiency and freedom and I think that the way that future frameworks will do that is without forcing a layer pattern on their adopters. JQuery is a perfect example where it is utility / helper pattern rather than a layered pattern where you can use JQuery but you must write all of your browser-side code in a special dialect of LISP invented by JQuery. But instead JQuery is a pure good – by choosing to use JQuery where appropriate – you lose nothing in the areas that you don’t choose to use JQuery.

  3. I should add that FOORM would work perfectly happily with Spring JDBC. Because FOORM is not a layer, It lets you pick whatever SQL layer you want. All my complaining about Spring JDBC is not because Foorm cannot or does not use it – it is more why I prefer Sakai’s SQL service to Spring JDBC. Sakai’s own SQL service does the things Spring JDBC does pretty much equivalently but I can fix the Sakai code when it is lacking and cannot fix the Spring code when it is lacking.

Leave a Reply

*