ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Using Castor JDO for SQL Mapping
Pages: 1, 2, 3, 4, 5, 6, 7

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.

Pages: 1, 2, 3, 4, 5, 6, 7

Next Pagearrow