Jenny the db code generator
This program reads a database and generates java source which
provides strongly typed access to the database.
Why was Jenny created?
In my opinion, there are two major problems with JDBC:
1) There are slight differences in SQL, drivers, app servers, database
access, etc. that make it clear that a facade is needed. The facade
would provide access to the database in plain java (not SQL). All of the
implementation specific code is concentrated in one small area instead
of spread out all over your application. The DBFacade class fixes this problem.
2) For any database change, it takes a great deal of discipline to find all
of the table names and column names used throughout your code that now
function differently. Even the slightest lack of discipline results in
a bug that might not be discovered until run time. Strong typing eliminates
this problem. This program (Jenny) provides the strong type checking.
For each table and view in a given database, Jenny will create a java source class file. Each of these classes will provide
a collection of general purpose methods for working with the table/view. They will also provide a list of all the column
names, the table/view name and a nested class called Row that can be used to manipulate a single row in the table.
Many tables have a primary key that is a non-nullable integer called "tablenameID" or sometimes "ID". If Jenny
finds this, she will add extra methods to the generated source for working with the primary key.
Some of my goals:
- Simplicity: Accessing the database needs to be as simple as possible. I decided that this meant using
static methods for the table level methods. All connections, statements, result sets and all other objects
that need closing are generally managed for you so you don't have to worry about it - thus eliminating about
70% of the code required for typical JDBC access.
- Unit Testing: I want to auto-generate a Mock class for every table class to facilitate unit testing.
This means that I need to have something I can override. Since static methods cannot be overridden, I need a
nested class that the static methods use that I can override for my mock classes. This also means that I need
to hide all of the Row constructors so a mock object can be returned. This allows unit testing without having
to provide SQL in the unit tests or to have a database server running during the testing.
- Flexibility:I want to be able to allow alternate connections, or to make several database calls with
one connection, so every database access method allows me to pass in a connection object. I also want to
allow multiple ways to get to the same database; support multiple databases on one database server; support
the use of multiple servers being used from one application.
- Functional Testing: I want to allow for functional testing (sometimes called integration testing -
kinda like unit testing, but with more than one class and sometimes the actual database) so I allow all
classes to replace the connection source at the facade level.
- Clear Division of Labor: I want to work with the idea that the database is managed by a DBA and business
logic is managed by software engineers. So fancy database stuff is done by the DBA in the database (i.e. joins
are handled within views by the DBA). In most big software shops, the software engineer will be accessing
data in an existing database that is managed by a DBA that has far more experience with the database than the
engineer. Anything that cannot be done through the facade probably needs to be done on the database side.
- Complexity Reduction: I want to avoid having complex relationships defined in an XML
document or embedded in the code. Good engineering is making complicated business logic look SIMPLE!
My experiences with object databases, object to relational mapping, CMP and similar tools is that they promise
simplicity, but when you get right down to it, it becomes horribly complicated!
- Self Discipline Not Required! Many solutions similar to Jenny require human engineers to have the
discipline to make sure that any changes in one place are reflected in other places. If your table name in
your database does not match the table name in your java code, you have a problem! With this solution, you
don't need that discipline. Therefore you have more reliable code.
What does Jenny do?
Each generated class will provide some basic methods such as these:
Row getRow()
Row getRow( String column , String searchText )
DBResults search( String column , String searchText , String[] dataColumns )
Row[] getRows( String column , String searchText )
Row[] getAllRows()
DBResults search( String[] dataColumns )
void update( String column , String searchText , Map data )
void delete( String column , String searchText )
void insert( Map data )
If an ID field is found, some methods like these will also be added:
Row getRow( int id )
void delete( int id )
Every class will have a nested class called Row that can provide strong
type checking for every field (column) as well as methods like:
void update()
void delete()
void insert()
The strong type checking for Row is provided by getters and setters.
Suppose you have a table called Employee. Jenny will generate a class called
EmployeeTable that will contain a Row class that might have the following methods:
int getEmployeeID()
void setEmployeeID( int employeeID )
String getLastName()
void setLastName( String lastName )
Here's a sample of a business logic method that uses a Jenny generated class:
// pass in an employee ID and get back the number of
// tax exemptions that the employee claims
private int getEmployeeExemptions( int employeeID )
{
return EmployeeTable.getRow( employeeID ).getExemptions();
}
This same code using plain JDBC could be 10 to 40 lines long depending on
how it would be implemented. You would need to get a connection, create a
statement, build your sql string, exceute your statement, wade through the
result set, and each of these things need try/catch/finally blocks! Don't
forget to close your connection, statement and result set!
How do I use Jenny?
Download jenny.zip
Unzip this - it contains two jar files, jenny-dev.jar, and jenny-prod.jar. Make sure your classpath includes both these files.
From the command line, type:
java com.javaranch.jenny.dev.Jenny db.properties
where db.properties is a properties file that describes how Jenny should
find your database. This is described further in the tutorial.
Tutorial on Using Jenny |
Jenny FAQ
|