The Source for Java Technology Collaboration
User: Password:



Bruce Tate

Bruce Tate's Blog

We should learn from Active Record

Posted by batate on January 19, 2006 at 09:42 AM | Comments (10)

There are at least two major styles of object-relational persistence frameworks. Mapping frameworks take an object-centered view of the world, and wrapping frameworks take a database-centered view of the world. Since Hibernate, Java developers have taken the mapping world view, pretty much across the board.

True, the mapping view has its advantages. The biggest one is the ability to map to those crusty legacy schemas, typically designed by that fresh gradute who did his dissertation on fourteenth-normal form. You can handle database-centric ideas like composite keys, and user defined types.

But there's a subtle undertone in the Java community that's going back to the basics. You can see indications of this movement in Spring's JDBC framework, and Clinton Begin's iBATIS framework. I often teach about persistence frameworks, and the most rapidly growing group of developers is those that want to explore JDBC extensions, like iBATIS and Spring JDBC. You just don't always need full ORM. And full ORM can get you in trouble when you don't have the skills to pull it off, as Ted Neward so pungently described in his Vietnam blog (I'd link to it, but the site has been down for a while.)

I'm not saying that ORM is the EJB of the 2000s. I just want to propose the idea that one size doesn't fit all, and sometimes, it's a good idea to admit the database. Which brings me to Active Record.

Java really needs something like Active Record. For the expected case, you can just create your schema, and let Active Record discover your relationships. So a person object winds up looking like this:

class Person < ActiveRecord::Base
  has_one :address
end

Ruby finds keys, foreign keys and table names through naming conventions (though you can override them if you want.) And you get all of the attributes created from scratch. Add a database column, and you don't have to change your code. Note that Ruby does something a little special here...it introduces attributes. With Java the experience would be different, but not impossible.

But the idea that my wrapping framework also eliminates repitition and extends through to relationships is very appealing to me. I can get down and dirty with SQL, or I can let the framework hide it from me. Admitting SQL is also one of the capabilities that's so attractive to iBATIS, and to a much lesser extent, Hibernate.

I also like the idea of reflecting through the model. I can ask any model class about an association on it. In this example, Person automatically gets an association called address. What table does it map to? I can say

Person.reflect_on_association(:address).active_record.table_name

I've built the basics for a simple report writer this way and it's nice. I can easily relate the table structure to the relationships of my object model. So to me, the lines between OR mapping and wrapping are blurring substancially.

Now throw in migrations. With a migration, you define a table in Ruby. Then, you have an .up method and a .down method. And you number the migrations so you can migrate schema and data in a single script. So here's a possible migration called 002_add_person.rb:

class AddPerson < ActiveRecord::Migration
  def self.up
    create_table "people" do |table|
      table.column "first_name", :string, :limit => 50
      table.column "last_name", :string, :limit => 50
      table.column "email", :string, :limit => 100
      table.column "company_id", :integer
      table.column "address_id", :integer
    end
  end

  def self.down
    drop_table "people"
  end
end

I can also add and drop columns and indexes. Active Record can take those migrations and translate them to SQL. So now, I'm pretty much independent of the underlying database version, if I want to be. And I can move up or down to any migration version:

rake migrate version=4

If I'm at 6, Rake will call 006*.down, and 005*.down. If I'm at 3, Rake will apply 004*.up. Migrations feel like Rails. I didn't expect to like them, but the idea just flows naturally.

Now, I'm not going to lie and say I'd be willing to jump through all kinds of hoops to try to map a legacy schema to a Rails project. I'd probably try to use Rails on Og, or Rails web services to a Java-backed project. But I do think that Active Record and Migrations solves it's target problem better than anything I've seen in the Java space.

So here's my point. Sometimes, it takes competition to force some interesting innovation. By using this stuff for a couple of weeks, my world view has changed. For a new project where I could define my own schema, or if I had an existing schema with object ids, I'd have a hard time moving back to the existing Java frameworks.

I'd love to see a strong Java implementation of Active Record. And I'd love to see a great generalized solution for migrations, wired into Ant. Sure, you can drive a screw in with a hammer. And sure, you can build a new schema with a mapping framework. But if you don't need to map, why bother? So the Java persistence community could learn a whole lot from Active Record. I'm a mapping bigot no more. Give me the best tool for the job.


Bookmark blog post: del.icio.us del.icio.us Digg Digg DZone DZone Furl Furl Reddit Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment

  • Hi Bruce, I'm not sure if such blatant self-promotion counts as spam, but I've been working on something similar to what you descibe. It's still early days, and it's part of larger framework. The model is built on top of Spring JDBC it maps tables and column names to Java classes and field names. There are groovy/ant scripts to generate the model (& controller & view) classes (including relations) for you. Hosted here on java.net the project is called Slingshot if you want to take a look.

    Posted by: johnmcclean on January 19, 2006 at 12:11 PM

  • I think Active Record is great for situations where you aren't and won't be dealing directly with legacy data, or any data other then what is directly relevant to your model (other then calling via web services etc, as you say). However, I think what makes it really great in rails is its handling of missing method definitions. Java is not a dynamic language, so what you end up with is stuff that looks like ResultSets, or DataSets etc: table.getField("name") etc... this is the approach that .Net has been using for some time. It just doesn't feel as nice in a non dynamic language, and I don't think it will. However, ORM is qutie mature, and even using it to do active record is fine (it just means that your source of truth for field definitions is in the objects, not in the tables like it is with RoR active record).

    Posted by: michaelneale on January 19, 2006 at 03:03 PM

  • Im going to have to learn ruby so I can read your code and comment. Right now it looks like a bunch of gibberish to me! :D leouser

    Posted by: leouser on January 20, 2006 at 07:37 AM

  • Hi... Like johnmcclean I have been working on the same kind of stuff. Mine generates the MVC classes and a CMS framework as well. The model is embedded with annotations for db mapping, ... lots of utils, etc. I have been faithfully ignoring the heavy dbo packages with the belief that the kiss rule would be remembered. Thanks for the post.

    Posted by: posttool on January 20, 2006 at 07:51 AM

  • It seems that as far as columns and relations go, you could either use naming conventions or use database metadata; many people have done that. The big difference with Ruby seems to be that with Java, Person.setName() needs to exist at compile time, period. As @michaelneale wrote, the other alternative is to use associative references everywhere (Person.set("name", newName)) and that could be based on reflected information. I'm not sure which part you like about RoR here--the fact that it can auto-detect/auto-guess DB schema information, or the fact that it can automatically generate attributes. The first is possible in Java, the second, not quite. Regards, Patrick

    Posted by: pdoubleya on January 20, 2006 at 09:53 AM

  • Thank you Bruce. I totally agree that Active Record is the way to go. I've been using iBATIS via Spring's adapter for some time. It's really slick.

    michaelneale, Bruce, What would be so bad about an ahead-of-time DAO/Active Record class generator. Have Java classes generated based on a database you point at (or some DDL scripts) -- all of the RoR Active Record rules would apply. You'd again be dealing with a Person object, and not a ResultSet. Granted you'd have to re-run the generation process whenever you alter the schema, but this could be intelligent (only process diffs) and happen quickly.

    There are benefits to using a dynamic lang, but seriously, I'm not going to burst into flames if I have to recompile/regenerate some classes. I *LIKE* type safety. Wouldn't the above solve 90% of the problem with 10% of the effort?

    What do you guys think?

    -Bryan

    Posted by: prime21 on January 20, 2006 at 01:54 PM

  • Bryan, I think Hibernate does this already. Have a look here: http://www.hibernate.org/hib_docs/tools/ant/index.html For the quick and dirty stuff, why not just use Ruby on Rails? Why do we need a Java implementation? Personally, I think the ORM approach offers a better long term approach than Active Record does.

    Posted by: damnhandy on January 21, 2006 at 01:20 PM

  • This comment will be done in somewhat ignorance of Ruby's implementation of Active Record and also somewhat out of ORM bigotry :)

    I'm completely for reducing code repetition and verbosity. In fact, using a more natural syntax for describing object associations such as RoR's "belongs_to" seems like a major advance to me. However, driving your application from a database model seems horribly wrong for several reasons:

    • Doesn't this approach limit our possibilities in evolving our object model, such as extracting separate classes from one that has been judged to be too big? The database schema will most likely outlast the object model, so there must be a way of mapping an evolving object model to a static schema.
    • Doesn't this approach increase the probability that the final domain model will be anemic and that the system will be a set of procedural-style transaction scripts manipulating dumb value objects? From my experience, designing a system with a such a strong focus on data definition ends up being far less OO than it should be.
    • Isn't readability greatly reduced by using this approach? This might be easy for the Ruby interpreter, but for me, switching between a Java IDE and a database visualizer to discover class attributes isn't my idea of productivity.
    • Isn't this paradigm strangely similar to the original and very inflexible Entity bean's model of "one table per class"?
    Don't get me wrong, I'm actually very happy that something as disruptive as RoR has come along - it will allow Sun and the Java community to take a closer look at the platform's productivity issues and hopefully to propose some improvements.

    Anyhow, I'd be very interested in knowing the Hibernate and Spring folks' opinions on the subject, as they are much more knowledgeable in this area than I am.

    Cheers,
    GB

    Posted by: gbilodeau on January 22, 2006 at 08:17 AM

  • Isn't this something you can write in a couple of hours in Java using JDBC metadata driving "dynamic" ResultSets, maybe even RowSets? I must be missing something.

    Posted by: michaelbushe on June 01, 2006 at 07:08 AM

  • Im going to have to learn ruby so I can read your code and comment. Right now it looks like a bunch of gibberish to me! :D leouser phone cards

    Posted by: actane on September 03, 2007 at 12:35 PM



Only logged in users may post comments. Login Here.


Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds