Using Command and Strategy Patterns for SQL Code Generation

By Kyle Brown, Knowledge Systems Corporation


he Problem I faced

One of the biggest parts of my job as an Apprentice Master at KSC is teaching students to build object systems that connect to a relational database. I've written extensively about this subject ([Brown 96a], [Brown 96b]), so I won't rehash much of that here. However, there is one purely mechanical aspect of my job that tends to drive me to distraction on a regular basis.

In the Apprentice Program, we set up a portion of our corporate network to resemble our client's environment as closely as possible. If a client is using a relational database, we must set up the appropriate database and give them access to it. Over the years in the apprentice programs, we've had students using SQL Server, Oracle, DB2, and just about any other database you'd care to name. In [Brown 95a], the development process derives our relational database designs from our object designs. That means that we determine the database schemas as part of our object design process. As you can imagine, in an iterative design process schemas tend to change quite a bit.

Why is it hard?

One of the most annoying parts of setting up and modifying database schemas is that while SQL is pretty well standardized across all vendors for querying and database creation, schema modification is decidedly not standardized. Perhaps it might be better to say that every database vendor has their own standard - they just haven't convinced any other vendors to switch over to their standard! Simple tasks like changing a column name, adding a column, or removing a column from a table require different commands on Oracle, SQL Server and DB2. Not only are the commands different, but each vendor has their own maintenance tool (SQL*Plus, SAF, etc.) that each have their own little quirks that you have to master.

The Solution

After spending long hours poring over manuals to find the obscure command to rename a column in one particular database, I finally decided to build a tool to handle this for me. What I was able to find was that judiciously applying a couple of design patterns, Command and Strategy, made it possible to build an extremely flexible system with only a few classes. Hopefully, explaining the design will clarify the meanings of these patterns for you, and give you some insights about how to apply them in your own programs as well.

I decided to implement the system in VisualAge for Java in order to get the power and flexibility that Java gives in dealing with different database connections. I also liked the potential of implementing the tool as either a standalone application or a three-tier applet/application pair that VisualAge provides. In a later article, I'll describe how some of the features of the JDBC (Java Database Connectivity specification) allow us to flexibly work with any database regardless of the peculiarities of any particular database API.

Metadata

The first step I took towards designing my tool was sketching out the primary domain objects in my system. Since I was dealing with a relational database, the two classes that stood out in my design were DbTableand DbColumn. Now, this may seem obvious to most of you, but believe me, this isn't necessarily a natural abstraction. I've often seen database tools that have no representation of the data elements apart from the actual tables and columns in the database. This sort of "map of the thing" separate from "the thing" is often referred to as Metadata. It's an extremely powerful concept, and gives us a lot of flexibility in our design.

I came up with the following design for my DbTable and DbColumn classes:

A DbTable contains a Vector of DbColumns. Each DbTable has a unique name. DbTable has methods to add and remove columns, and to find a column with a particular name. A DbColumn is simply a triplet of a name, a datatype, and a boolean indicating if it is required (NOT NULL) or not. These two classes form the heart of our system. As you will see later, the rest of the classes in the system exist to manipulate these two classes in different ways.

Command

The next set of classes that I developed was a set of classes to represent the different actions that I could possibly take on a database table. In my particular case, I was concerned with adding columns to a table, removing columns from a table, and renaming a column. There are many other possibilities that I could have implemented, but these three covered the bulk of the work that I needed to do. The question arises now - why did I need to implement these as classes? Aren't these just behaviors of a Table or Column? In fact there are corresponding behaviors in my DbTable and DbColumn classes, but I need to take a short digression to explain why I needed to implement these actions as classes.

I envisioned myself and my apprentices as using the tool in the following way: I would pick a table from a list of tables in a particular database, and then would see a list of the different columns in the table, along with the information about those columns. I would want to be able to select any column, choose to modify or delete it, and be able to add columns to the list. Most interactive applications like this also feature some sort of undo/redo facility. It's nice to be able to "back out" of an action that you didn't intend - like deleting a column when you really meant to rename it. The standard design pattern that you can apply in cases like this is the Command pattern from [Gamma 94]. The intent of the Command pattern states you can "Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations".

This is a very useful facility and one that I wanted in my design. But there was another reason that I wanted to use the Command pattern completely separate from the undo/redo facility. The way that SQL databases change a database schema involve making a series of "deltas" or changes to the particular database as a series of SQL commands. For instance, if you want to add a column to a table, you would use the ALTER command in SQL. As an example, if you had an employees table and wanted to add a salary column, you would use the following command in ANSI standard SQL:

 ALTER employees ADD salary money NOT NULL WITH DEFAULT

Each different action (add, remove, rename) has a different set of commands that you have to execute to perform the action. Therefore, while the table representation is useful for viewing what your final database table will look like, the commands are necessary to actually get to the representation you want. So, what I arrived at was a hierarchy that looked like the following:

The methods applyTo(DbTable aTable) and backoutFrom(DbTable aTable) support changing a DbTable to incorporate a particular change, and undoing that change respectively. The method generateSQLWith(SQLGenerator gen, DbTable table) is responsible for creating and executing the correct SQL statements that actually change a database table to its new form.

As an example, let's look at how the applyTo() method is implemented in the class AddColumn:

  public void applyTo(DbTable table) {  table.addColumn(column);
}

Pretty straightforward, eh? ThebackoutFrom() method is equally simple - it's implemented by calling removeColumn()with the command's column. As you can imagine, the DeleteColumn command is implemented by simply switching the addColumn() and removeColumn() messages between the applyTo() and backoutFrom() methods. RenameColumn is also very simple. It holds onto the old column name and the new column name. It's applyTo()method finds the column with the old name and renames it to the new name, while its backoutFrom() method does the opposite.

The generateSQLWith() methods in the three command classes are a little harder to understand. To understand how they are implemented, we need to take a closer look at our problem and investigate another design pattern: Strategy.

Strategy

As I mentioned earlier, one of the annoying aspects of schema maintenance is that the actual SQL commands to add, delete and rename columns differ from database to database. For instance, the command to rename a column in SQL Server is very simple:

  sp_rename tableName.oldName,tablename.newName

On the other hand, accomplishing the same task in Oracle requires a bit more work:

  CREATE TABLE temporary ( newColumnName, col1, col2, …) 
  AS SELECT oldName, col1, col2, … FROM tableName
  DROP TABLE tableName
  RENAME temporary to tableName

We need to be able to encapsulate the particular database specifics in our design so that we can operate correctly on each database. One potential way in which we could do this is to make subclasses of each command class for each database. We could have a OracleAddColumnclass and a SybaseAddColumn class and override the method generateSQLWith() in each. However, this makes adding a new database a real pain. We would have to add new subclasses of each command class every time we wanted to add a new database - a lot of unnecessary work for little benefit.

A better solution is to use the Strategy design pattern from [Gamma 94]. Strategy's intent is to "Define a family of algorithms, encapsulate each one and make them interchangeable. Strategy lets the algorithms vary independently from clients that use it." The last bit - about letting the algorithms vary independently from the clients is exactly what we want. We want to detach our database dependencies from the particular commands that the user has created. The following diagram will begin to show how we can accomplish this:

We have an abstract superclass SQLGenerator and subclasses for each particular database. Note that this only adds one new class for each database, not the many necessary in the other solution. Each class implements methods to build the particular SQL for a given command in that database. For instance the SqlServerSQLGenerator class would generate SQL based on the sp_rename command we described above. On the other hand, the OracleSQLGenerator class would know to generate the more complex SQL shown in the other example. Let's try to tie this together and make it a little more concrete.

The implementation of generateSQLWith() in RenameCommand looks like the following:

public void generateSQLWith(SQLGenerator gen, DbTable table) {
  gen.buildRenameSqlFor(this,table);
}

If the first parameter passed in was an instance of SqlServerSQLGenerator, the following method would be called:

public void buildRenameSqlFor(RenameColumn renameCommand, DbTable sourceTable){
   StringBuffer spRenameCommand = new StringBuffer();
   spRenameCommand.append("sp_rename "); 
   spRenameCommand.append(renameCommand.getOldName());
   spRenameCommand.append(','); 
   spRenameCommand.append(renameCommand.getNewName());
   execute(spRenameCommand.toString());
}

The implementation in OracleSQLGenerator looks similar, but instead creates three StringBuffers, one for each SQL statement, and executes them separately.

The End Result

Putting the different pieces together, we arrive at a final design that looks like the following:

There is one additional class in the final design that we haven't discussed - TableBuilder. A TableBuilder does just that - it builds tables using the Commands that we've seen. It contains a Stack of Commands and allows the user to undo actions by simply popping them from the top and undo-ing them against the current DbTable. As you might have guessed, it's an implementation of another pattern - in this case the Builder pattern from [Gamma 94].

Final thoughts

That's it for this time. Hopefully, you can now see a little better how Command and Strategy can work with code generation. Also, you have seen how keeping a separate metadata representation of an entity can make understanding how to work with it easier. As I mentioned earlier, in a later article we'll return to this example and examine how we can use the JDBC features in VisualAge for Java. We'll look at creating DbTables from existing system table information regardless of the actual database we're working against and how to execute the SQL we've generated. A simplified version of this code that doesn't execute the SQL against a database is available from http:\\www.ksccary.com. If you have any comments or questions, please feel free to contact me at kbrown@ksccary.com.

References

[Brown 96a] Brown and Whitenack, "Crossing Chasms: A pattern language for Object-Relational Integration",Pattern Languages of Program Design, Vol II, John Vlissides, Jim Coplien, and Norman Kerth Eds., Addison-Wesley, 1996 (http://www.ksccary.com/ordbjrnl.htm)

[Brown 96b] Brown and Whitenack, "A Pattern Language for Relational Databases and Smalltalk", Object Magazine, October 1996 (http://www.ksccary.com/pldbjrnl.htm)

[Gamma 94] Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides, Design Patterns: Reusable Elements of Object Oriented Software, Addison-Wesley Publishing Company, Reading MA. (http://st-www.cs.uiuc.edu/patterns/DPBook/DPBook.html)

Enjoy the article? Subscribe to Eye on Objects!

Kyle Brown is a Senior Member of Technical Staff at Knowledge Systems Corporation (KSC). Kyle has been building systems in Smalltalk, large and small, for over seven years. During this time he has taught the principles of Smalltalk programming and OO design through KSC's Smalltalk Apprentice program, as well as through conference presentations and magazine articles. He is the co-author (with Bobby Woolf and Sherman Alpert) of the upcoming Addison-Wesley book "Design Patterns: The Smalltalk Companion".

Feel free to contact Kyle Brown

Home Page