What is Torque?

Torque was developed as part of the Turbine Framework (version 2.1). It is now decoupled and can be used by itself. Starting with Turbine 2.2 the coupled version of Torque is deprecated. You should upgrade to use the decoupled version .

Torque is a utility packaged with Turbine that generates all the database resources required by your web application. Torque uses a single XML database schema to generate the SQL for your target database and Turbine's Peer-based object relation model representing your XML database schema. Additionally, an HTML document describing the database can be generated if you wish to make a browseable version of the database schema.

Torque is currently deployed in two forms: a stand-alone version and a version that is bundled with the Turbine Developer's Kit. Both versions are identical in function, but have slightly different configurations.

Torque Directory Structure

Here is what the Torque directory structure looks like. There are slight differences between the stand-alone and TDK versions but nothing significant:

torque/
    lib/              <--  Jar files required by Torque (stand-alone version)
    dtd/              <--- DTD for Torque XML database descriptors.
    schema/           <--- Project specific XML database descriptor.
    templates/        <--- Velocity templates used for source generation.
    xsl/              <--- XSLT stylesheets use for HTML generation.
    output/           <--- Target location for output (stand-alone version).

    build.xml         <--- Ant build file that controls Torque.
    build.properties  <--- Properties file that controls Torque behaviour.

A typical user of Torque would really only ever need to edit the build.properties file in base directory, and the project-schema.xml in the schema directory. We'll quickly go over each of these files and some of options you have using Torque.

Quick Start Guide

For those who just want to see Torque go all you have to do is select your target database and target package in build.properties , edit the project-schema.xml to suit your needs, then ant . That's it!

You will probably want to set the project property in the build.properties file eventually so that your generated sources have names that reflect your project name, but you can do that later :-). You also have to remember that if you change the project property that you have to change the name of your project XML database schema to match! For example if you change the project property from the default value of "project" to "killerapp" then you must change the name of the default schema in the schema directory from project-schema.xml to killerapp-schema.xml .

Configuring Torque

You'll have to modify the following two files to configure Torque. The first is a list of all the properties currently supported by Torque:

build.properties

Property Description
project The name of your Turbine project. The project name is reflected in the names used for the generated output. If your project is set to "killerapp" then you will get the following files:

killerapp-schema.sql

killerapp.generation.report

Remember when changing this property to change the name of your XML database schema to match!
database The target database for your Turbine project. This is used in conjuction with SQL generation.
extend Used in conjuction with the OM generation.
mapname Used in conjuction with the OM generation.
suffix Used in conjuction with the OM generation.
targetPackage Used in conjuction with the OM generation.
databaseUrl Used by the JDBC -> XML process, and by the SQL Ant Task that will initialize your target database with the generated SQL.
databaseDriver Used by the JDBC -> XML process, and by the SQL Ant Task that will initialize your target database with the generated SQL.
databaseUser Used by the JDBC -> XML process, and by the SQL Ant Task that will initialize your target database with the generated SQL.
databasePassword Used by the JDBC -> XML process, and by the SQL Ant Task that will initialize your target database with the generated SQL.
You should not have to edit any properties below here!
configDir The directory Torque looks in for the build.properties file.
templatePath The path to the Velocity templates used for source generation.
SQLControlTemplate Control template used for SQL source generation.
OMControlTemplate Control template used for object model source generation.
idTableControlTemplate Control template used for Id Broker source generation.
outputDirectory Directory where the generated sources are placed.
schemaDirectory Directory where Torque looks for XML database schemas.

project-schema.xml

This is an example of what the XML database schema might look like. This particular example is a snippet of the database used for Turbines role-based user system:

<database>

  <table name="ID_TABLE">
    <column name="ID_TABLE_ID" required="true" primaryKey="true" type="INTEGER"/>
    <column name="TABLE_NAME" required="true" size="255" type="VARCHAR"/>
    <column name="NEXT_ID" type="INTEGER"/>
    <column name="QUANTITY" type="INTEGER"/>

    <unique>
      <unique-column name="TABLE_NAME"/>
    </unique>

  </table>

  <table name="TURBINE_PERMISSION" idMethod="idbroker">
    <column name="PERMISSION_ID" required="true" primaryKey="true" type="INTEGER"/>
    <column name="PERMISSION_NAME" required="true" size="99" type="VARCHAR" javaName="Name"/>

    <unique>
      <unique-column name="PERMISSION_NAME"/>
    </unique>

  </table>

  <table name="TURBINE_ROLE_PERMISSION">
    <column name="ROLE_ID" required="true" primaryKey="true" type="INTEGER"/>
    <column name="PERMISSION_ID" required="true" primaryKey="true" type="INTEGER"/>

    <foreign-key foreignTable="TURBINE_ROLE">
      <reference local="ROLE_ID" foreign="ROLE_ID"/>
    </foreign-key>

    <foreign-key foreignTable="TURBINE_PERMISSION">
      <reference local="PERMISSION_ID" foreign="PERMISSION_ID"/>
    </foreign-key>
  </table>

</database>

Please refer to Torque Schema Reference to find out more about the the different elements and attributes.

The Object Relational Map

The OM/Peer (Torque) system can map a class hierarchy into a single table. This is 1 of 3 methods generally described in object-relational mapping designs. Its benefits include that it does not require joins in order to gather the attributes for a single object. It falls short in modelling a class hierarchy where the related classes have a non intersecting collection of attributes, as in this case a row in the table will have several null columns.

A Class Hierarchy

       A
       |
     -----
    |     |
    B     C
    |
    D

There are two ways that are built into the torque generated Peers in order to specify what class a particular row in the table A represents. A row will need to have some information that can distinguish the class. You should specify the column in the table that serves this purpose with the attribute "inheritance"

<table name="A"...>
  ...
  <column name="FOO" inheritance="single" type="VARCHAR".../>
</table>

In this case you would need to specify the full className in column FOO, so the valid values of FOO would be:

com.mycompany.project.om.A
com.mycompany.project.om.B
com.mycompany.project.om.C
com.mycompany.project.om.D

This is slightly inefficient in storage and also generates some inefficient code in the Peers because the Peer cannot know what classes are available until it gets each record and so will call Class.forName(FOO's value) for each row.

The efficiency can be improved in the case where the class hierarchy is known, which would be in most circumstances. So you can specify the classes in the xml specification:

<table name="A"...>
  ...
  <column name="FOO" inheritance="single" type="CHAR" size="1"...>
    <inheritance key="B" class="B" extends="com.mycompany.project.om.A"/>
    <inheritance key="C" class="C" extends="com.mycompany.project.om.A"/>
    <inheritance key="D" class="D" extends="com.mycompany.project.om.B"/>
  </column>
</table>

where in the above we are using NULL (or any other value) to stand for class "A". An numeric column could also be used for the key. Using the above method, torque will cache a copy of each class, so the Class.forName is only done during APeer's initial load into memory.

Overriding the Default Behavior

The following example comes from Scarab (an issue tracking system). In Scarab a class hierarchy definition is described in a few tables, this provides for an evolving hierarchy. This arrangement can be provided for using the following extensions. In the xml specification, the column responsible for determining the class is marked using the inheritance="single" attribute.

<table name="SCARAB_ISSUE_ATTRIBUTE_VALUE" idMethod="none"
        javaName="AttributeValue">
    <column name="ISSUE_ID" primaryKey="true" required="true"
            type="INTEGER"/>
    <column name="ATTRIBUTE_ID" primaryKey="true" required="true"
            type="INTEGER" inheritance="single"/>
    <column name="OPTION_ID" required="false" type="INTEGER"/>
...
    <foreign-key foreignTable="SCARAB_ISSUE">
        <reference local="ISSUE_ID" foreign="ISSUE_ID"/>
    </foreign-key>
    <foreign-key foreignTable="SCARAB_ATTRIBUTE">
        <reference local="ATTRIBUTE_ID" foreign="ATTRIBUTE_ID"/>
    </foreign-key>
    <foreign-key foreignTable="SCARAB_ATTRIBUTE_OPTION">
        <reference local="OPTION_ID" foreign="OPTION_ID"/>
    </foreign-key>
...
</table>

It might be interesting to note that the column responsible for the determining the class is also a primary and foreign key. Marking the column this way will cause torque to generate an BaseAttributeValuePeer.getOMClass method.The code in this method will be attempting to create a class from the information provided in column which is an integer. This is obviously wrong, but it gives us a method to override to provide the correct information.

So in AttributeValuePeer, we override the method:

/**
 * Get the className appropriate for a row in the
 * SCARAB_ISSUE_ATTRIBUTE_VALUE table
 */
public static Class getOMClass(Record record, int offset)
    throws Exception
{
    NumberKey attId = new NumberKey(record.getValue(offset-1 + 2)
                                        .asString());
    Attribute attribute = Attribute.getInstance(attId);
    String className = attribute.getAttributeType().getJavaClassName();

    TurbineGlobalCacheService tgcs =
        (TurbineGlobalCacheService)TurbineServices
        .getInstance().getService(GlobalCacheService.SERVICE_NAME);

    String key = getClassCacheKey(className);
    Class c = null;
    try
    {
        c = (Class)tgcs.getObject(key).getContents();
    }
    catch (ObjectExpiredException oee)
    {
        c = Class.forName(className);
        tgcs.addObject(key, new CachedObject(c));
    }
    return c;
}

where in the above method, we use the foreign key(s) to traverse the tables to get the class information. Then we cache the Class to avoid the inefficiency of Class.forName on each row. (We also cache the contents of the class hierarchy tables, since the dataset is quite small and static.)