Installing and Using the Torque Object Mapper

Each table definition begins by specifying the name of the table. Next, each column is defined. Note that PITCHER_ID is flagged as both autoIncrement and primaryKey. This means that when a new row is created, the database will automatically increment this value. It also means that the developer can't write out a new value with this column specified; it will always be generated automatically.

After the columns are defined, any foreign keys are listed. Although this may not be required from the perspective of the database (MySQL doesn't support foreign keys, for example), it is also a flag to Torque to create relationships between the two objects, as you will see later.

In the case of the TEAM table, we need to specify idMethod="none" because the teams are loaded in by hand, and the team ID numbers are explicitly specified. The "none" value tells Torque that although there are columns that are primary keys, Torque should not attempt to manage them.

Finally, the table that holds the actual game statistics. In this case, we still have to specify idMethod="none", this time because there are no primary keys at all, and Torque gets confused if you don't explicitly tell it not to manage such a table. This table also has three separate foreign keys.

Once the schema is written, you use Ant to have Torque generate both the Java and the SQL files for the application.

With the database built and populated, we can talk about what's happening on the Java side. If you look in the src subdirectory, you'll see two directories inside it. One, "sql", contains the automatically generated SQL used to create the database. The other, "java", is the head of a source tree that contains the object mapping implementation. Here's the layout:

src
├───java
│ └───pitchers
│ └───torque
│ └───map
└───sql

All the files that we care about live in the torque directory. For each table, there are four classes defined in this directory. For example, the TEAM table has four corresponding classes: BaseTeam, BaseTeamPeer, Team, and TeamPeer.

The Base files contain the actual implementation details of the table, and are re-created each time you do an ant build. The Peer class is something like a static method; it's used to deal with the table as a whole. The non-Peer class is the implementation of a row from this table. This will become clearer when you see them in use.

The non-Base classes start out empty. For example, here is TeamPeer.java:

package pitchers.torque;
import java.util.*;
import com.workingdogs.village.*;
import org.apache.torque.map.*;
import org.apache.torque.pool.DBConnection;
// Local classes
import pitchers.torque.map.*;
/**
* The skeleton for this class was autogenerated by Torque on:
*
* [Thu Aug 01 23:35:05 EDT 2002]
*
* You should add additional methods to this class to meet the
* application requirements. This class will only be generated as
* long as it does not already exist in the output directory.
*/
public class TeamPeer
extends pitchers.torque.BaseTeamPeer
{
}

As you can see, all it does is extend the Base class. The important thing to know about these two files is that they are not overwritten on rebuilds, so you can put extensions and business logic in them.

The first extension you could add is a helper function for the Team class to allow you to look up a team by name (remembering that the unique identifier for teams is not the team name but an integer sequence, so that teams can easier change names if they are bought or sold). Here's the source for Team.java:

This code illustrates several features of Torque. To begin with, it uses the Criteria class. Criteria are equivalent to the WHERE clause in an SQL statement. Each Peer class has static string variables that correspond to the columns of that table. So in this case, after creating a new Criteria, you can add the condition that the team name must equal the name passed in to the method (if you only pass two arguments to the "add" method, the EQUAL test is assembled).

Next, TeamPeer.doSelect is called, with the Criteria as its argument. The doSelect method returns a List with zero or more elements, depending on how many rows in the database matched. Since there should only ever be a single team in the database with a given name, the code returns null if either zero or more than one row is returned; otherwise, it returns the team.

The Team class is basically a bean with getters and setters for all the columns, as well as knowing how to save itself or create new records.

The only other class that you need to extend is the Pitcher class, to add some business logic and helper functions.

The findPitcherByName method is essentially the same as the findTeamByName method you've already seen, except that it shows an example of a compound Criteria. After adding the first condition using the Criteria.add method, all subsequent conditions should either use Criteria.and or Criteria.or, to specify how the WHERE clause should be constructed.