Using Castor JDO for SQL Mapping
Binding Through Class Methods
There are two basic ways of binding the properties of a class to columns
in a SQL table. The first method is to use public class methods to access the
properties. By default, Castor uses this type of access, inferring the name
of the public accessor/mutator methods from the name of the field being
mapped. As was shown earlier, if the field name is id, then the
accessor method is assumed to be getId().
What if the method names are different from what Castor assumes? In that
case, you have to inform Castor of the proper methods by specifying them in
the field mapping element:
<class name="ShippingLine" identity="id">
<map-to table="shipping_line"/>
<field name="id" type="integer" key-generator="seqgen"
get-method="getID" set-method="setID">
<sql name="id" type="integer"/>
</field>
<field name="name" type="string" required="true">
<sql name="name" type="varchar"/>
</field>
</class>
Normally, Castor would be looking for the Java bean methods
getId() and setId() associated with the id
property. The class author (me) prefers to capitalize both letters of ID when
it occurs in method names. Because the capitalization is different from what
Castor would expect, the method names must be given explicitly in the mapping
file.
Binding Through Class Data Members
A second way to bind a class property to a SQL table column is to access
the class data member directly, rather than through methods. The data member
must be public for this mechanism to work. To specify direct access, the
following attribute of the field element is used:
<class name="ShippingLine" identity="id">
<map-to table="shipping_line"/>
<field name="_id"type="integer" key-generator="seqgen" direct="true">
<sql name="id" type="integer"/>
</field>
<field name="name" type="string" required="true">
<sql name="name" type="varchar"/>
</field>
</class>
By accessing the data member directly in this way, you can reduce the
number of public methods in the class definition. Notice that I've had to add
the underscore character, since I'm now referring to the name of the data
member directly, and it has an underscore in its name. The drawback to this
mechanism is that, because of the need for a public data member, I
consequently allow direct access by outside class methods in the application.
In any case, you really don't have much protection from the wicked, but at
least with public get/set methods you can deprecate their use where advisable
through Javadoc comments.
Mapping Extended Classes
In Java, a class definition can extend another (base) class' definition
through type-derivation. The new class will inherit all of the data members and
methods of the base class, plus it might add a few of its own.
In the case where only the extended class is stored in the database, the
mapping file entry for that class will be the same as for any other class;
however, if the base class is by itself persisted in the database already,
then there will be an entry in the Castor mapping file for that class. It
would be silly to have to copy all of those field mappings for the base class in
the new mapping entry of the derived class. It would also be a maintenance
worry: the derived class definition may change, and you'd wind up with two
mapping entries to update.
Better would be to store the new data members of the extended class in a
new table in the database using a new mapping, and store the existing members
of the base class in the table already set aside for it. Then Castor can
perform a join operation between the two tables, generating a row that has
all of the values of the data members (both inherited and new) for the extended
class. Two joined table rows form one class instance.
The extends Attribute
A mapping for a class extends another class mapping through use of the
extends attribute. Let's say I want to subtype the ship
class so that there are cargo ships and passenger ships. I then have the
following Java classes:
public class Ship
{
String name;
String registry;
public Ship() { ... }
//getters/setters...
}
public class CargoShip extends Ship
{
int cargoTonnage;
public CargoShip() { ... }
//getters/setters...
}
public class OilTanker extends Ship
{
int barrels;
public OilTanker() { ... }
//getters/setters...
}
To store these new classes, I can map them as follows:
<class name="Ship" identity="registry name">
<map-to table="ship"/>
<field name="name" type="string" required="true">
<sql name="name" type="varchar"/>
</field>
<field name="registry" type="string" required="true">
<sql name="registry" type="varchar"/>
</field>
<!-- rest offields mapped here -->
</class>
<class name="CargoShip" extends="ship" identity="id" key-generator="seqgen">
<description>Cargo is measured in net tonnage</description>
<map-to table="cargo_ship"/>
<field name="id" type="integer">
<sql name="id" type="integer" />
</field>
<field name="cargoTonnage" type="integer">
<sql name="cargo_tonnage" type="integer"/>
</field>
</class>
<class name="OilTanker" extends="ship" identity="id" key-generator="seqgen">
<description>Oil is measured in barrels</description>
<map-to table="oil_tanker"/>
<field name="id" type="integer">
<sql name="id" type="integer" />
</field>
<field name="barrels" type="integer">
<sql name="barrels" type="integer"/>
</field>
</class>
Because new tables are required to store the additional data members of
the extended classes, each extended class must have its own id, in addition to
the identifying name/registry it already inherits from the base Ship class.
If we were dealing only with Java classes, having these extra identifiers in
the extended classes would be silly. But since we store the extended class
data in two relational tables, each row in the second (extension) table must
have a unique identifier; otherwise, the table would not be in First Normal
Form (where each row must be distinct). The unique identifer in each row
ensures that such is the case.
Mapping Multiple Classes to One Table
There can be cases where you might not want to create a new database table
for each class to be persisted. It's possible in Castor to store data from a
parent object and its child in one table.
The ships of the line, in addition to ferrying ubiquitous shipping
containers across the seas, have to transport an odd-sized crate now and
then. The dimensions of each crate are maintained in the database. The unit
of measure of a dimension might be in either meters or feet.
public class Dimension
{
public double measurement;
public UOMType uom;
}
public class UOMType
{
private static String _feet = "ft";
private static String _meters = "m";
private String _unit;
public static UOMType FEET = new UOMType( _feet );
public static UOMType METERS = new UOMType( _meters );
private UOMType( String type ) { _unit = type;}
public void setUnit( String type ) {_unit = type; }
public String getUnit() {return _unit;}
}
The last class maintains public instances that denote two types of
measurement. Each Dimension instance will refer to one of
these UOMType objects. Since UOMType is a separate class
from Dimension, it would seem that
I'd have to have two class descriptions in the Castor mapping file. That's
overkill for the tiny UOMType class, with its single primitive value.
An alternative approach is to store UOMType data in the same table that
contains the Dimension rows. To accomplish this, I use a dot path in the
class mapping for Dimension, like so:
<mapping>
<!-- Mapping for class Dimension -->
<class name="Dimension" identity="id">
<map-to table="dimension"/>
<field name="id" type="integer">
<sql name="id" type="integer"/>
</field>
<field name="measurement" type="double">
<sql name="measurement" type="decimal"/>
</field>
<field name="uom.unit" type="string">
<sql name="uom" type="varchar"/>
</field>
</class>
</mapping>
A dot path denotes data that is nested at a lower level in a container
class than the class that is being mapped. By referring to nested data in
this way, both the Dimension class data and the
UOMType class data are stored
in one table. With this approach, it's easy to look at the database row
values and see what is being measured.