Skip to content
Print

An Easier Java ORM Part 4

30
Jul
2007

In keeping with my ActiveObjects series, here’s part 4. In this part, we’ll look at schema generation/migration and the ever-interesting topic of pluggable name converters and English pluralization.

One of ActiveObjects’s main concepts is that you (the developer) should never have to worry about the semantics of database design. You should simply be given the tools to design your models in a natural and object-oriented way, and the database just sort-of takes care of itself. To allow this sort of simplicity, the database schema has to be automatically generated, leaving nothing to the developer in this area. Fortunately, ActiveObjects does poses this capacity.

Schema Generation

In a nutshell, schema generation in ActiveObjects works by parsing the specified entity interfaces. First, a dependency tree is built, ensuring that the schema generation occurs in the proper order satisfying all dependent tables. ActiveObjects does generate all foreign keys for you, ensuring data integrity and maximum performance. This of course has the unfortunate side effect that everything must be inserted in the proper order; hence the tree.

Next, the dependency tree is passed through a loop which iterates through it and invokes DatabaseProvider#render(DDLTable), which generates the database-specific DDL statements necessary to create the entity-corresponding schema.

This all sounds fine-and-dandy on paper (or in this case, screen), but when you actually try to implement it in a real-world senario it gets a bit sticky. For one thing, Java’s types are nowhere near as robust as the ANSI SQL types. Additionally, almost every DDL allows developers to put certain restrictions on fields such as default values, auto incrementing or even forcing table-unique values. The solution, avoiding XML and other non-Java meta-programming, is to use annotations:

public interface Person extends SaveableEntity {
    public String getFirstName();
    public void setFirstName(String firstName);
 
    @Unique
    @SQLType(precision=128)
    public String getLastName();
 
    @Unique
    @SQLType(precision=128)
    public void setLastName(String lastName);
 
    @SQLType(Types.DATE)
    public Calendar getBirthday();
    @SQLType(Types.DATE)
    public void setBirthday(Calendar birthday);
 
    @Accessor("url")
    public URL getURL();
    @Mutator("url")
    public void setURL(URL url);
}
 
// ...
manager.migrate(Person.class);    // generates and executes the appropriate DDL

It might seem a bit ugly and annotation-ridden, but it does the job well and in pure-Java. There are more annotations which could be used (@OnUpdate, @Default, etc…), but these suffice to give a basic example.

You’ll notice right off the bat that each annotation has to be applied to both the accessors and the mutators. This is for two reasons. First, Java reflection doesn’t guarantee any particular ordering in which it will return the methods for a Class<?>. Thus, either the accessor or the mutator could be reached first and used to generate that field’s DDL, meaning the metadata needs to be applied to both or it could possibly be ignored by the schema generator. Second, ActiveObjects allows for read-only or write-only fields, meaning you don’t need the full accessor/mutator pair to access the database, one will suffice. This could be useful for application static data or for fields you just-plain don’t want the application to be able to mutate directly. Because ActiveObjects doesn’t assume an accessor/mutator pair, it can’t just wait for both the accessor and the mutator to be parsed. Thus, meta is required on both.

The other point of interest in this example is the use of the @Accessor and @Mutator annotations. From very early on, ActiveObjects has allowed developers to specify non-conventional method-names as database fields. In this case, ActiveObjects would normally assume the getURL method corresponded to to the “uRL” field (case intentional). This is because AO will assume the default Java get/set/is convention and recase the method accordingly. Since we obviously don’t want that for the “url” field, we use the @Accessor and @Mutator annotations to override ActiveObjects’s field name parser.

Pluggable Name Converters

By default, ActiveObjects uses a very simple set of heuristics to determine the table name from the entity class name. Essentially, this heuristics boil down to a simple camelCase implementation. For example, the “Person” entity would correspond to the “person” table. A “BillingAddress” entity would be mapped to “billingAddress” and so on. This is nicely conventional, but certainly not everyone would agree with this style of table naming. This is why ActiveObjects provides a mechanism to override the table name conversion and specify a custom algorithm.

The name converter interface is pretty simple. It has a single method (getName(Class<? extends Entity>):String) which is where the actual “meat” of the conversion happens. It also defines four algorithmically optional methods, which are intended to be used by developers as a quick-and-easy way to specify explicit mappings (for example, sometimes you want to override the algorithm for a specific class or name pattern without adding the @Table annotation to the entity). These methods are important, but could be left as stubs for a custom name-converter used “in house”.

To make it easier to write custom name converters, an abstract superclass has been created which handles most of the “boiler plate” functionality. I would recommend that this superclass (AbstractNameConverter) be used in lieu of a direct implementation of PluggableNameConverter. The only difference is that instead of overriding the getName(Class<? extends Entity>):String method from the superinterface, the method to override is getNameImpl(Class<? extends Entity>):String. This is the method called by AbstractNameConverter if the entity class in question fails to match an existing mapping.

Specifying the name converter to use is as simple as a single method call to EntityManager:

manager.setNameConverter(new MyNameConverter());

From that moment onward, all operations handled by that EntityManager instance (including schema generation) will use the specified pluggable name converter. This allows us to do some interesting things which would be otherwise out of reach…

English Pluralization

Table name mappings like {Person => person} and {BusinessAddress => businessAddress} may be conventional and nicely deterministic, but most people like database designs which reflect the underlying data a bit better. Since tables by definition contain multiple rows, it only makes sense that their names should be pluralized. Since we now have a mechanism to override the default name converter, we should be able to create an implementation which handles this pluralization for us for an arbitrary English word.

To accomplish this, ActiveObjects defines a class PluralizedNameConverter in the “net.java.ao.schema” package which extends the default CamelCaseNameConverter. The actual implementation within the name converter is fairly simple. All the algorithm needs to do is load an ordered .properties file (included with ActiveObjects) which contains all of the pluralization mappings as regular expressions. (e.g. “(.+)={1}s”) These mappings are then added to the name mappings implemented in AbstractNameConverter, which handles the semantic details of mapping the generated (in CamelCaseNameConverter) singular CamelCase names to their pluralized equivalents. Thus, PluralizedNameConverter doesn’t really do much work at all, the real action is in the abstract superclass as it handles the actual logic defined in englishPluralRules.properties. This is where the real interest lies. However, detailing all of the ins and outs of English pluralization rules would extend this article even farther beyond the breaking point; so I will have to revisit the topic in a future post.

To use the English pluralization for your own projects, simply initialize your EntityManager instance in the following way:

EntityManager em = new EntityManager(jdbcURI, username, password);
em.setNameConverter(new PluralizedNameConverter());
// ...

Now, the Person class will map to the “people” table. Likewise, the BusinessAddress entity will correspond to the “businessAddresses” table. Just like magic! :-)

It’s not a silver bullet, and I’m sure you’ll come across situations where the pluralization will be invalid. This is one of the reasons why manually adding mappings to name converters is possible. My only request is that you send your mappings my way so that I can include them in the properties file, allowing others to benefit from your wisdom.

However, despite all the rules (both manual and default) which can be specified, automatic English pluralization is still a very inaccurate science. Thus, the decision was made not to make pluralization the default name conversion method for ActiveObjects, in contrast to the same decision with ActiveRecord, which is pluralized by default. Hopefully, by not forcing pluralization upon you, I’ve made database design with ActiveObjects a little less stressful than it otherwise could have been. :-)

Conclusion

In conclusion: I write blog posts that are way too long. I hope this taste of automated schema generation and name conversion algorithms was helpful and useful to you in your quest for that ever-elusive, intuitive ORM.

(P.S.)

One of the things I’m currently working on for the upcoming ActiveObjects 0.4 release is the concept of schema migrations. Migrations are a little different than a straight schema generation as they don’t eliminate pre-existing data, but merely convert the existing tables to match the current schema definition. This is a mind-bogglingly useful feature in database refactoring, a common activity early in the application design process. Unfortunately, it’s also rather hard to do correctly. If you’d like to check up on my progress, criticize my coding style, or just play with the latest-and-greatest version, feel free to checkout ActiveObjects from the SVN: svn co https://activeobjects.dev.java.net/svn/activeobjects/trunk/ActiveObjects

Comments

  1. Very nice. I really look forward details on initial migration features in trunk version. How it can be used/tested? Please give some brief details. I have compiled version from trunk so I can test.

    Also its a bit off-topic, how @ManyToMany scenario is supposed to beeing used? U have User and Role tables and need ManyToMany relationship between them. How to setup it?

    Thanks,
    David

    David Marko Tuesday, July 31, 2007 at 1:10 am
  2. The new migration code will work the same way as the old generating code. So, you would call the EntityManager#migrate(Class…) method. This would handle all of the migration tasks (including diffing the existing schema, and CREATE/ALTER/DROP to match the new one). There’s an Ant task which doesn’t work (at least, I don’t think it does), and command line invocation is also supported.

    Good question with the relationship annotations. I’ll dash off a quick article today explaining how they work, as well as the @Ignore annotation, which I just realized I forgot to mention in today’s post.

    Daniel Spiewak Tuesday, July 31, 2007 at 1:33 am
  3. Argg, WordPress ate my comment. Suffice it to say, it’s the “migrate” method within EntityManager. :-)

    Daniel Spiewak Tuesday, July 31, 2007 at 1:34 am
  4. Just used ActiveObjects from trunk and tried migration feature. On firt run AO created a new table correctly. Then I changed a JAVA class(add some additional property) and called migrate once again. This is what a got:(subdomain is a JAVA bean a store in database)

    org.postgresql.util.PSQLException: ERROR: relation “subdomain” already exists
    at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:1548)
    at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1316)
    at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:191)
    at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:452)
    at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:337)
    at org.postgresql.jdbc2.AbstractJdbc2Statement.executeUpdate(AbstractJdbc2Statement.java:283)
    at org.apache.commons.dbcp.DelegatingStatement.executeUpdate(DelegatingStatement.java:228)
    at net.java.ao.schema.Generator.migrate(Generator.java:150)
    at net.java.ao.EntityManager.migrate(EntityManager.java:143)

    David Marko Tuesday, July 31, 2007 at 2:32 am
  5. Yeah, I would expect that. Right now in trunk/ the migrations feature isn’t fully wired up. I haven’t actually written the “glue code” to connect the migrate() method to the schema reading and diffing features. In other words, it’s trying to create the schema all over again each time you call the method. :-S

    Daniel Spiewak Tuesday, July 31, 2007 at 7:43 pm
  6. Daniel,

    I apologize because I have not read all 4 parts before asking questions. Does AO require an external connection pooling implementation or do you have CP built-in inside AO? Thanks

    Kevin Hoang Le Friday, August 17, 2007 at 4:32 pm
  7. It requires an external pooling *library*, but integration with these libraries is built in. All you have to do is put the pooling library on your classpath, and ActiveObjects will use it. For example, I use commons-pool/commons-dbcp by placing the JARs on the classpath and then calling new EntityManager(uri, username, password) just as before. I could also use C3P0 or Proxool if I wanted to. Alternatively, I could leave the pool libraries completely off the classpath, and ActiveObjects would use the JDBC driver directly, without pooling.

    daniel Friday, August 17, 2007 at 4:48 pm
  8. Thanks, but in your built-in integration, how do pick up the configuration, such as the max number of connections, an SQL statement to ping the DB if supported by the CP, other properties, etc. Or is there an overloaded version of EntityManager() where you could pass in the additional above-mentioned properties? Thanks

    Kevin Hoang Le Friday, August 17, 2007 at 5:05 pm
  9. Hmm, ActiveObjects out of the box sort of hides all that from you, so you can’t get to it directly. The idea is if you want to do anything advanced with either the pooling or the JDBC drivers, you’ll need to extend either the existing pool provider, or create a new pool provider yourself. This will allow you to completely control all aspects of the database pooling and even implement a library yourself. The downside to this is you must then manually pass this database provider to EntityManager thusly:

    DatabaseProvider provider = new MyPoolProvider(new MySQLDatabaseProvider(uri, username, password));
    EntityManager manager = new EntityManager(provider);

    The “manager” instance will then use the MyPoolProvider functionality to encapsulate its database access. In fact, this is actually how the new EntityManager(String, String, String) auto-magical constructor works under the surface, just with the addition of two enums which allow auto-detection of database driver and available pool providers.

    Oh, a pool provider is actually just a DatabaseProvider which delegates all of its functionality to a specified DatabaseProvider instance with the exception of the getConnection() method, which is handled in a pool-specific way. There is a PoolProvider abstract super-class which makes the implementation of new pool providers fairly trivial.

    daniel Friday, August 17, 2007 at 5:12 pm
  10. Hello!

    I have just started using Java and ActiveObjects for a library project. The goal is to use perisistant objects not in case of an application but get easy access via a lib. What I would like to develop is kind of typical access without a client need of EntityManager.

    Person me = new Person();
    me.setName(“Christian”);
    me.save();

    Person i = new Person(“name=?”, “Christian”);
    System.out.println(i.getName);

    Since I am a Java newbee too, how can I extend the interface with constructors?

    Thank You Christian

    Christian Wednesday, February 27, 2008 at 8:42 am
  11. There’s no way that you’re going to be able to get rid of EntityManager entirely (this restriction is fairly standard for ORMs). This is because to even create the instances of the interface, EntityManager must be invoked. You don’t actually extend Entity with a class, but rather with an interface (which cannot be instantiated).

    Daniel Spiewak Wednesday, February 27, 2008 at 11:04 am
  12. Thanks for your fast Reply Daniel.

    If every do things in same way is not a proof that it is a good solution also :-) Hmmm seems … if I realy want to get rid of the em I have to write some kind of wrapper classes. Maybe I will do this in future. Since I will need the lib for beanshell scripts maybe its a bit easier to handle the objects.

    Christian

    PS I realy like ActiveObjects – its my favior ORM. I have tested for a few days Apache Cayenne, Oracle TopLink, Hibernate and ActiveObjects.

    Christian Thursday, February 28, 2008 at 12:29 am
  13. Have you seen this one: http://www.mollypages.org/dbo ?

    foo Friday, April 24, 2009 at 7:25 pm

Post a Comment

Comments are automatically formatted. Markup are either stripped or will cause large blocks of text to be eaten, depending on the phase of the moon. Code snippets should be wrapped in <pre>...</pre> tags. Indentation within pre tags will be preserved, and most instances of "<" and ">" will work without a problem.

Please note that first-time commenters are moderated, so don't panic if your comment doesn't appear immediately.

*
*