Jaxor Tutorial by Ryan Cuprak

I first heard of "Jaxor" at Martin Fowler's November presentation for COOUG. At the time I had been looking for a java persistence framework to reduce mindless coding and testing as well as to speed up my development cycle. In my mind one of the chief benefits of Jaxor is that it is simple and compact. Unlike other packages, it does not have a tremendous learning curve nor do I forsee it being a support nightmare should I ship a product using it. In other words, if my code is part of a legacy system eight years from now and jaxor has been discontinued, another developer could easily read and understand the code. With that being said, I have created this short tutorial to get others up and running. I am by no means an expert - just a Jaxor beginner sharing his knowledge!
I. Setup
This tutorial covers a basic test client (SimpleJaxor.tar.gz). In order to run the code in this example you will need a database and the jdbc driver for that database. If you do not already have a database setup, I would recommend Frontbase which I used. After downloading the tutorial code, edit the database.properties for your local database settings. To run the code, only the database.properties file has to be edited for your local settings. The ant script (discussed shortly) will construct the tables, compile, and run the Test Client. You will require Ant if you want to compile/run the tutorial - follow the instructions for installation.

II. Introduction
Jaxor is an object relational mapping package. Conceptually can be thought of as a code generator and a set of classes from which the generated code derives its functionality. The generated classes are specifiied in xml files. The code generation functionality is wrapped in an Ant task. When developing with Jaxor you will go through the following steps:
  1. Create XML files describing the object relational mapping
  2. Generate the classes (using an Ant script)
  3. Code to the generated code (write your entity beans that use the generated code).
  4. Compile both your code with the generated code
  5. Write Test cases (not covered)

When using Jaxor, I would not recommend editing the generated classes as jaxor is meant to facilitate rapid development- when the database changes, xml files are changed and code regenerated. Changes made to the generated classes is thus lost. Furthermore it would be a good idea to keep the generated classes partitioned into their own package so that they can be easily purged if a table is dropped otherwise manual intervention is required.

III. Project Overview
Figure 1 shows the project structure as seen in IntelliJ's IDEA IDE. If you are using IDEA you may want to add file mappings for .jaxor which are xml and .registry which is a regular properties file (text). The build.xml file will work from the commandline.

At this point you may want to poke around the build.xml file to see how the project is built. There are seven targets in the build.xml file:

  • clean - deletes all temporary files, including generated code.
  • setup - dependent on clean, creates the build directories.
  • generate-code - dependent on setup, runs the jaxor ant task and generates the persistance code.
  • compile-generated-code - compiles the entire project and code generated by the jaxor ant task.
  • create-database - creates the database
  • destroy-database - destroys the database tables
  • run-test-client - runs the test client (database must be clean, run destroy-database and then create-database)

Only the 'run-test-client' task requires that the database be constructed, and clean. When you first download the project and want to see it in action, run the tasks in the following order: generate-code -> compile -> create-database -> run-test-client.

The database schema for this project is quite simple. There are two tables, user and project. The project table has a foreign key on the users table. Each table will map to a conceptual object (specified in project.jaxor and user.jaxor)- a user entity and a project entity. The project object will return a user object corresponding to the foreign key, but only if the client requests it. The project's pointer to a user entity will be loaded lazily.
Figure 2
Running the generate-code task will generate the following code and place it in net.cuprak.jaxor.generated:
  • ProjectEntity (interface) - represents a project record from the database.
  • ProjectFinder - factory for finding a project and creating a new project
  • ProjectImpl - implements ProjectEntity
  • ProjectIterator
  • ProjectList
  • UserEntity (interface) - represents a user record from the database
  • UserFinder - factory for finding an entity and creating a new entity
  • UserImpl - implements UserEntity
  • UserIterator
  • UserList

The TestClient in net.cuprak.jaxor.application is written to use the generated classes. Below is the method responsible for creating a new user and project entity and inserting them into the database. Basic sequence of events for this test client:
  • load database settings from properties file and create a connection factory (in ejb the factory would acquire a connection from jndi and not bother with the property file)
  • create a UserEntity with a user_id of '1' - set attributes
  • insert the new user into the database
  • create a Project Entity with a project_id of '1' - set attributes
  • insert the new project into the database

A couple of things must be noted, the UserEntity/ProjectEntity (respective implementations) are serializable which means you can throw them across the network via rmi. The objects are thus Data Objects and can be used as such (refer to J2EE Core Patterns).

If you are developing using EJB - an Entity Bean would wrap the generated entity object (ProjectEntity/UserEntity in this case) and a session bean would wrap the finder object (ProjectFinder/UserFinder in this case).

IV. Tutorial in Stages
The basic layout of the tutorial project has been covered and I will focus on how the simple tutorial application was developed.
Step 1 - Connection Factory & AbstractEntity
Unlike other object-relation bridges, you supply the jdbc connection to the generated classes. To accomplish this:
  1. Write a connection factory.
  2. Write an abstract base class extending AbstractEntity. All generated classes will extend this abstract base class. Custom functionality which is common to all generated classes (or group of generated classes) should be placed within in this class. At a minimum this abstract base class must implement the methods pertaining to jdbc connections.

In the tutorial application, there is a ConnectionFactory interface (Snippet 2) with a single 'getConnection' method. There are two classes which implement this interface: ClientConnectionFactory (Snippet 3) and EJBConnectionFactory. The ClientConnectionFactory is to be used by standard desktop applications. The database driver settings are passed in via the constructor.

Snippet 2 - Connection Factory
Snippet 3 - ClientConnectionFactory
The SimpleBaseEntity (Snippet 4) has three methods, two for acquiring a connection and one for setting the factory. Note, the ConnectionFactory instance is stored as a static variable so that a client class can invoke a static finder method to locate entities.
Snippet 4 - SimpleBaseEntity
Step 2 - XML Mapping Descriptors
The next step is to write the xml files which describe the entities to be creates to be created and how they map to tables within the database. There will be a total of four descriptor files for this simple tutorial:
  1. generator.xml - contains attributes that are common to multiple tables and also specifies the interfaces/classes which the generated classes implement/extend.
  2. mapper.registry - A java properties file which specifies datatype mappings- a string representation to the object which handles it. The .jaxor files reference the types in the registry file.
  3. project.jaxor - Defines the project entity to be created.
  4. user.jaxor - Defines the user entity to be created.

Note, there is nothing that precludes a project from having multiple generator.xml/mapper.registry/AbstractEntity base classes etc. I do not want to mislead anyone into thinking that there can only be one instance of these files. In practice it would probably be a good idea to partition each logical unit of your database into separate units each with their own set of descriptor files to facilitate re-use and ease maintenance.

2.1 Generator.xml
The generate.xml is used to specify the interface/base class the generated is to implement/extend. It is also a place to place attributes which are common across all tables - this capability is not used in this tutorial. The entity interfaces generated by jaxor (UserEntity and ProjectEntity in this case) will extend the interface specified which is net.sourceforge.jaxor.EntityInterface here. The implementation of UserEntity and ProjectEntity will extend net.cuprak.jaxor.base.SimpleBaseEntity which was discussed in step 1.
generator.xml
2.2 Mapper.registry
The mapper.registry file is a properties which specifies a datatypes and the classes which handle the datatypes. In the case of this tutorial there was no need to add or modify the standard mappings supplied by jaxor. In a future tutorial I will detail how to add a new data type. You would add a datatype where you want to map a datatype in the database to a specific type, like numeric to a money class, blob to an image, etc.
mapper.registry
2.3 User.jaxor
The user.jaxor defines the user entity which maps to the user table in the database. As shown in figure 2 a user has one primary key and two attributes - firstname and lastname. The primary key (user_id) is an integer. The firstname and lastname attributes are variable length characters. All fields are fields are required (can't be null). The user entity jaxor creates will throw an exception if any fields aren't set when insert is invoked unless the attribute is marked optional (none are in this tutorial). An entity can have more than one primary key. The user.jaxor also specifies which package in which the generated classes should be placed. On the attribute tags, the type must be found in the mapper.registry file otherwise you will recieve an error. The name on attribute must correspond exactly to the name of the field the database otherwise you will recieve an sql exception (wrapped in a jaxor exception).
user.jaxor
2.4 Project.jaxor
The project.jaxor defines the project entity which maps to the project table in the database. As shown in figure 2 a project has one primary key, three attributes one of which is a foreign key on the user table. The primary and foreign keys are both integers while the name and description fields are strings. All fields are required. Handling the foreign key is a little tricky. The simplest way of handling it would be to just return the integer and require the client code to then perform the lookup. However that is not an elegant solution. A better approach is to return an entity to which the foreign key refers. To do that, the actual method calls to perform that activity are embedded in the XML file within a field element.
The field element defines getcreated_by_user() and setcreated_by_user(UserEntity) methods. The name of the property is taken verbatim from the field element with the type of the set/get being specified in the type attribute. The get method calls the static selectByPrimaryKey on UserFinder which is generated as a part of the User entity (2.3). Defining the set method is not as obvious as the UserEntity instance variable is pulled from the string 'UserEntity' without the 'entity' portion and all lowercase. Furthermore, the property containing the integer representation of the foreign key is created by removing the underscores and capitalizing the first character of every word - quite unlike how the field element behaves. So the attribute 'created_by_user_id' becomes 'createdByUserId' - this behavior is the same for all attributes but not field elements.
Step 3 - Generating the Source
Now that the objects have been defined, it is now time to generate the source. To generate the source code, use the jaxor ant task. The taskdef element defines the class of jaxor ant task and the classpath. The jaxor ant task has several dependencies such as xerces and velocity which must be in the supplied classpath.
The parameters the jaxor ant task takes are fairly straight forward. Every time this task is run, it will overwrite all previously generated entities - that is why the generated classes should not be modified. The srcdir takes the source directory containing the .jaxor files. The mappers attribute takes the "mapper.registry' file and the configfile requires the 'generator.xml' file. The include element is used so that none of the other xml files or java class files are mistaken for jaxor description files. To improve build time, explicitly specify the directories containing the jaxor files.
Step 4 - Using the generated Source
Once the source is successfully generated and compiled, unit test code should be written to test the mapping and make sure that the entities correctly map to the database.
Conclusion
The five development steps for jaxor can be summarized as:
  1. Create XML files describing the object relational mapping
  2. Generate the classes (using an Ant script)
  3. Code to the generated code (write your entity beans that use the generated code).
  4. Compile both your code with the generated code
  5. Write Test cases (not covered)