Wednesday, March 08, 2006
Crossing borders: Exploring Active Record
07 Mar 2006
The Java™ programming language has had an unprecedented run of success for vendors, customers, and the industry at large. But no programming language is a perfect fit for every job. This article launches a new series by Bruce Tate that looks at ways other languages solve major problems and what those solutions mean to Java developers. He first explores Active Record, the persistence engine behind Ruby on Rails. Active Record bucks many Java conventions, from the typical configuration mechanisms to fundamental architectural choices. The result is a framework that embraces radical compromises and fosters radical productivity.
2005 was in many ways a strange year for me. For the first time in nearly a decade, I began to do serious development in programming languages other than the Java language. While working with a startup, a business partner and I had some incredible success with a quick proof of concept. We were at a crossroads -- should we continue Java development or switch to something radical and new? We had many reasons to stay on the Java path:
- We'd need to learn a new language from scratch.
- The Java programming community was so strong that we'd have trouble getting our customer to accept a switch.
- We would not have the thousands of Java-centric open source projects to choose from.
![]() |
|
But we didn't dismiss the idea of developing in another language and began to build our application in Ruby on Rails, a Web application framework built on the Ruby language. We were successful beyond our wildest imaginings. Since then, I've been able to split time between Java teaching and development (mostly with Hibernate and Spring), and Ruby teaching and development. I've come to believe that it's critical to learn other approaches and languages from time to time, for these reasons:
- Java is not the perfect language for every problem.
- You can take some new ideas back to your Java programming.
- Other frameworks are shaping the way that Java frameworks are built.
In this article, the first in a series that aims to demonstrate these ideas, you'll look at Active Record, the persistence architecture at the heart of Ruby on Rails. I'll also throw in a discussion of schema migrations.
Active Record: Radically different
When I took Rails out for a test drive, I freely admit my attitude was more than a little arrogant. I didn't think Active Record was up to the job, but I've since learned that it offers everything I need for some problems.
The best way to show you how Active Record handles persistence is to write some code. My examples use the MySQL database, but with minor changes, you can use the code with other databases as well.
By far, the easiest way to use Active Record is through Ruby on Rails. If you want to follow along in code, you need to install Ruby and Rails (see Resources).
Then, you can create a project. Go to the directory where you want Rails to create a new project, and type:
|
Rails creates your project, and you can use Active Record through some nifty Rails tools. The only step that remains is configuring your database. Create a database called people_development
and edit the config/database.yml file to look like this (make sure you type in your username and password):
|
You needn't worry about the test and production environments in this article, so that's all of the configuration you need. You're ready to go.
![]() |
|
In Hibernate, you'd usually begin development by working on your Java objects because Hibernate is a mapping framework. The object model becomes the center of your Hibernate universe. Active Record is a wrapping framework, so you start by creating a database table. The relational schema is the center of your Active Record universe. To create a database table, you can use a GUI or type this script:
|
Creating an Active Record class
Next, create a file called app/models/people.rb. Make it look like this:
|
This Ruby code creates a class called Person
with a superclass called ActiveRecord::Base
. (For now, assume Base
is like a Java class and that ActiveRecord
is like a Java package.) Surprisingly, that's all you need to do right now to get a good amount of capability.
Now you can manipulate Person
from within the Active Record console. This console lets you use your database-backed objects from within the Ruby interpreter. Type:
|
Now, create a new person
. Type these Ruby commands:
|
If you haven't worked with Active Record before, chances are you're seeing something new and interesting. This small example encapsulates two important features: convention over configuration and metaprogramming.
Convention over configuration saves you from tedious repetition by inferring configuration based on names you choose. You didn't need to configure any mapping because you built a database table that followed Rails naming conventions. Here are the main ones:
- Model class names such as
EmailAccount
are in CamelCase and are English singulars. - Database table names such as
email_accounts
use underscores between words and are English plurals. - Primary keys uniquely identify rows in relational databases. Active Record uses
id
for primary keys. - Foreign keys join database tables. Active Record uses foreign keys such as
person_id
with an English singular and an_id
suffix.
Convention over configuration gains you some additional speed if you follow Rails conventions, but you can override the conventions. For example, you could have a person
that looks like this:
|
So convention over configuration doesn't unnecessarily restrict you, it just rewards you for consistent naming.
Metaprogramming is Active Record's other major contribution. Active Record makes heavy use of Ruby's reflection and metaprogramming capabilities. Metaprogramming is simply writing programs that write or change programs. In this case, the Base
class adds attributes to your person
class for every column in the database. You didn't need to write or generate any code, but you could use person.first_name
, person.last_name
, and person.email
. You'll see more extensive metaprogramming as you read on.
Active Record also includes some features that many Java frameworks don't have, such as model-based validation. Model-based validation lets you make sure the data within your database stays consistent. Change person.rb to look like this:
|
From the console, load person
(because it's changed) and type these Ruby commands:
|
Ruby returns false
. You can see the error message for any Ruby property:
|
![]() |
|
So far, you've seen capabilities that you don't find in many Java frameworks, but you might not be convinced yet. After all, the hardest part of database application programming is often managing relationships. I'll show you how Active Record can help. Create another table called addresses
:
|
You've followed the Rails convention for your primary and foreign keys, so you'll get the configuration for free. Now change person.rb to support the address relationship:
|
And you should create app/models/address.rb:
|
I should clarify this syntax for readers new to Ruby. belongs_to :person
is a method (not a method definition) that takes a symbol as a parameter. (Look at a symbol as an immutable string for now.) The belongs_to
method is a metaprogramming method that adds an association, called person
, to address
. Take a look at how it works. If your console is running, exit it and restart it with ruby script/console
. Next, enter the following commands:
|
Before I talk about the relationship, look again at the find
method, find_by_email
. Active Record adds a custom finder for each of the attributes. (I've oversimplified things a bit, but this explanation works for now.)
Now, look at the last relationship. has_one :address
adds an instance variable of type Address
to person
. The address is persisted as well; you can verify by entering Address.find_first
in the console. So Active Record is actively managing the relationship.
You're not limited to simple one-to-one relationships, either. Change person.rb to look like this:
|
Make sure you pluralize address
to addresses
! Now, from the console, type these commands:
|
The person.addresses <<> command adds the
address
to an array of addresses
. Active Record added a second address
to person
, as expected. You verified that there was one more record in the database. So has_many
works like has_one
, but it adds an array of addresses
to each Person
. In fact, Active Record lets you have a number of different relationships, including these:
belongs_to
(many-to-one)has_one
(one-to-one)has_many
(one-to-many)has_and_belongs_to_many
(many-to-many)inheritance
acts_as_tree
acts_as_list
composition
(mapping more than one class to a table)
From the very beginning, Active Record has helped to evolve my understanding of persistence. I learned that wrapping approaches aren't necessarily inferior; they're just different. I'd still lean on mapping frameworks for some problems, like crufty legacy schemas. But I think Active Record has quite a large niche and will improve as the mappings supported by Active Record improve.
I also learned that an effective persistence framework should take on the character of the language. Ruby is highly reflective and uses a form of reflection to query the definition of database system tables.
But, as you'll see now, my Rails persistence experience did not begin and end with Active Record.
![]() |
|
Evolving schema independently with migrations
As I learned Active Record, two problems plagued me. Active Record forced me to build create-table SQL scripts, tying me to an individual database implementation. Also, in development, I would often need to delete the database, which forced me to import all of my test data after each major change. I was dreading my first post-production push. Enter migrations, the Ruby on Rails solution for dealing with changes to a production database.
With Rails migrations, I could create a migration for each major change to the database. For this article's application, you could have two migrations. To see the feature in action, create two files. First, create a file called db/migrate/001_initial_schema.rb and make it look like this:
|
And in 002_add_addresses.rb, add this:
|
You can migrate up by typing:
|
rake
is like Java's ant
. Rails has a target called migrate
that runs migrations. This migration adds your entire schema. Go ahead and add a few people, but don't worry about the addresses yet.
Now suppose you've made a terrible mistake and need to migrate down to a previous version. Type:
|
That command took the first migration (denoted by the version number in the filename) and applied the AddAddresses.down
method. Migrating up calls up
methods on all necessary migrations, in numerical order. Migrating down calls the down
methods in reverse order. If you look at your database, you'll see only the people
table. The address has been removed. So migrate
lets you move up or down based on your needs.
Migrations have another feature: dealing with data. You can migrate up again by typing:
|
This command runs AddAddresses.up
and, in the process, initializes each Person
object with an Active Record address. You can verify this behavior in the console. If you've added Person
objects, you should also have Address
objects. Open a new console and count the number of person and address database rows, like this:
|
So migrations deftly handle both schema and data. Now you can look at how these ideas translate to what's happening on the Java side.
![]() |
|
Java technology's persistence history is at once fascinating, tragic, and hopeful. Years of bad choices in the Java language's core persistence framework -- Enterprise JavaBeans (EJB) versions 1 and 2 -- led to years of struggling applications and disillusioned users. Hibernate and Java Data Objects (JDO), which both form the foundation of the new EJB persistence and a common persistence standard, led to a rise in object-relational mapping (ORM) within the Java community, and now the overall Java experience is a much more pleasant one.
The Java community has had a seven-year love affair with ORM, a.k.a. mapping frameworks. Fundamentally, a mapping approach lets a user define Java and database objects independently and build maps between them, as in Figure 1:
Figure 1. Mapping frameworks

Because the Java language is often an integration language first, mapping plays an important role for integrating crufty legacy systems, even those created long before any object-oriented languages existed. The Java community now embraces mapping to an incredible degree. Today, a typical Java programmer reaches for ORM to solve even basic problems. We like mapping because the Java mapping implementations often beat the Java implementations for alternative approaches: wrapping frameworks.
But you shouldn't disregard the power of wrapping frameworks. A wrapping framework places a thin wrapper around a database table, converting database rows to objects, as in Figure 2:
Figure 2. Wrapping frameworks

Your mapping layer carries a good deal of overhead if you don't need a map. And Java wrapping frameworks are seeing something of a resurgence. The Spring framework does JDBC wrapping and integrates features to do all kinds of enterprise integration. iBATIS wraps the result of a SQL statement instead of a table (see Resources). Both frameworks are brilliant pieces of work and underappreciated, in my opinion. But the typical Java mapping framework does things automatically that Java wrapping frameworks force you to do manually.
The Java platform already boasts state-of-the-art mapping frameworks, but I now believe that it needs a groundbreaking wrapping framework. Active Record relies on language capabilities to extend Rails classes on the fly. A Java framework could possibly simulate some of what Active Record offers, but creating something like Active Record would be challenging, possibly breaking three existing Java conventions:
- A persistence solution should work only on a Java POJO (plain old Java object). First and foremost, it would be difficult to create properties based on the contents of a database. A domain object might have a different API. Instead of calling
person.get_name
to set a property, you might useperson.get(name)
instead. At the cost of static type checking, you'd get a class built of metadata driven from a database. - A persistence solution should express configuration in XML or annotations. Rails bucks this trend through forcing naming conventions with meaningful defaults, saving the user an incredible amount of repetition. The cost is not great because you can override defaults as needed with additional configuration code. Java frameworks could easily adopt the Rails convention-over-configuration paradigm.
- Schema migrations should be driven from the persistent domain model. Rails bucks this convention with migrations. The core benefit is the migration of both data and schema. Migrations also allow Rails to break the dependence on a relational database vendor. And the Rails strategy decouples the persistence strategy from the issue of schema migrations.
In each of these cases, Rails breaks long-standing conventions that Java framework designers have often held as sacred. Rails starts with a working schema and reflects on the schema to construct a model object. A Java wrapping framework might not take the same approach. Instead, to take advantage of Java's support for static typing (and the advantages of tools that recognize those types and provide features such as code completion), a Java framework would start with a working model and use Java's reflection and the excellent JDBC API to dynamically force that model out to the database.
One new Java framework that's moving toward sophistication as a wrapping framework is RIFE (and its subproject RIFE/Crud), created by Geert Bevin (see Resources). At the core, RIFE's persistence has three major layers, shown in Figure 3:
- A simple JDBC wrapper, which provides a callback-style implementation of JDBC through templates
- A set of database-independent SQL builders offering an object-oriented approach to query building
- A type-mapping layer that can convert most SQL types to most related Java types
Figure 3. Architecture of persistence for the RIFE framework

These layers are used by simplified APIs, called query-managers, which provide intuitive access to persistence-related tasks such as save and update. One is specific to JDBC, and the other provides an analogous API that detects metadata related to RIFE's content-management framework.
RIFE/Crud sits on top of all of these frameworks, providing a very small layer that groups all of them together. RIFE/Crud uses constraints and bean properties to build an application's user interface, the site structure, the persistence logic, and the business logic automatically. RIFE/Crud relies heavily on RIFE's metadata capabilities to generate the interface and the relevant APIs, but it still functions with POJOs. RIFE/Crud is completely extensible through RIFE's clearly defined integration points in its API, templates, and component architecture.
The query-managers' API is strikingly simple. Here's an example of RIFE's persistence model in action. Suppose you have an Article
class and you want to build a table structure around it and persist two article
s to the database. You use this code to get a query manager, given a data source:
|
Next, create the table structure in the database by installing the query manager:
|
Now you can use the query manager to access the database:
|
So, like Active Record, the RIFE framework uses convention over configuration. Article
must support an ID property called id
, or the user must specify the ID property using RIFE's API. And like Active Record, RIFE uses the capabilities of the native language. In this case, you still have a wrapping framework, but the model drives the schema instead of the other way around. And you still have a much simpler API than most object-relational mappers for many of the problems RIFE needs to solve. Better wrapping frameworks would serve Java well.
![]() |
|
Active Record is a persistence model written in a non-Java language that takes good advantage of that language's capabilities. If you've not seen it before, I hope this discussion has opened your eyes to what's possible in a wrapping framework. You also saw migrations. In theory, Java frameworks could adopt this concept. Though mapping frameworks have their place, I hope you'll be able to use this knowledge to look beyond the conventional mapping solutions in the Java language and catch the wrapping wave. Next time you'll take a look into continuation-based approaches to Web development.
![]() |
|
Learn
- Beyond Java (Bruce Tate, O'Reilly, 2005): The author's book about the Java code's rise and plateau and the technologies that could challenge the Java platform in some niches.
- "Rolling with Ruby on Rails" and Learn all about Ruby on Rails: Learn more about Ruby and Rails, including installation procedures.
- "Ruby on Rails and J2EE: Is there room for both?" (Aaron Rustad, developerWorks, July 2005): This article compares and contrasts some of the key architectural features of Rails and traditional J2EE frameworks.
- "Ruby off the Rails" (Andrew Glover, developerworks, December 2005): Andrew Glover digs beneath the hype for a look at what Java developers can do with Ruby, all by itself.
- Active Record: Active Record is the persistence framework for the Ruby on Rails framework.
- "Improve persistence with Apache iBATIS and Derby" (Daniel Wintschel, developerWorks, January 2006): Learn all about iBATIS, one of Java's best wrapping frameworks, in this three-part tutorial series.
- "The Spring series, Part 2: When Hibernate meets Spring" (Naveen Balani, developerWorks, August 2005): This article teaches the best combination for using Hibernate -- Spring with Hibernate.
- The Java technology zone: Hundreds of articles about every aspect of Java programming.
Get products and technologies
- The RIFE framework: RIFE uses some of the more radical techniques from non-Java languages.
- Ruby on Rails: Download the open source Ruby on Rails Web framework.
- Ruby: Get Ruby from the project Web site.
Discuss
- developerWorks blogs: Get involved in the developerWorks community.
![]() |
|
![]() | ||
![]() | Bruce Tate is a father, mountain biker, and kayaker in Austin, Texas. He's the author of three best-selling Java books, including the Jolt winner Better, Faster, Lighter Java. He recently released Spring: A Developer's Notebook. He spent 13 years at IBM and is now the founder of the J2Life, LLC, consultancy, where he specializes in lightweight development strategies and architectures based on Java technology and Ruby. |
source:http://www-128.ibm.com/developerworks/java/library/j-cb03076/?ca=dgr-lnxw01ActiveRecord