Codehaus     ExoLab     OpenEJB     OpenJMS     OpenORB     Castor     Tyrex     
 

Main
  Home
  About
  Features
  Download
  API
  Schema
  Castor Forums
  Mailing Lists
  CVS / Bugzilla
  Support
  CastorWiki

XML
  Using XML
  Source Generator
  Schema Support
  XML Mapping
  XML FAQ
  Custom Handlers

JDO
  Using JDO
  JDO Config
  Types
  JDO Mapping
  JDO FAQ
  Other Features

Advanced JDO
  OQL
  Trans. & Locks
  Design
  KeyGen
  Long Trans.
  Nested Attrs.
  Pooling Examples
  LOBs

More
  Presentations
  The Examples
  3rd Party Tools
  JDO Tests
  XML Tests
  Configuration
  Tips & Tricks
  Full JavaDoc
  CastorWiki
  
  

About
  License
  Contributors
  Status, Todo
  Changelog
  Library
  Contact
  Project Name

  



Castor JDO Mapping

Documentation Author(s):
Bruce Snyder


Introduction
The Mapping File
    The <class> element
    The <map-to> element
    The <field> element
    The <sql> element
Examples
    Java class files
    DDL
    Object Mappings
        Read-only fields
        1:1 relation
        1:M relation
        M:N relation


Introduction

The Castor mapping file also provides a mechanism for binding a Java object model to a relational database model. This is usually referred to as object-to-relational mapping (O/R mapping). O/R mapping bridges the gap between an object model and a relational model.

The mapping file provides a description of the Java object model to Castor JDO. Via Castor XML, Castor JDO uses the information in the mapping file to map Java objects to relational database tables. The following is a high-level example of a mapping file:

      <mapping>
          <class>
              <map-to />
              <field>
                  <sql />
              </field>
              ...
          </class>
      </mapping>
      

Each Java object is represented by a <class> element composed of attributes, a <map-to> element and <field> elements. The <map-to> element contains a reference to the relational table to which the Java object maps. Each <field> element represents either a public class variable or a the variable's accessor/mutator methods (depending upon the mapping info). Each <field> element is composed of attributes and one <sql> element. The <sql> element represents the field in the relational table to which the <field> element maps.

It is possible to use the mapping file and Castor's default behavior in conjunction. When Castor handles an object but is unable to locate information about it in the mapping file, it will rely upon its default behavior. Castor makes use of the Java programming language Reflection API to introspect the Java objects to determine the methods to use. This is the reason some attributes are not required in the mapping file.

The Mapping File

The <class> element

        <!ELEMENT class ( description?, cache-type?, map-to?, field+ )>
        <!ATTLIST class
                  name            ID       #REQUIRED
                  extends         IDREF    #IMPLIED
                  depends         IDREF    #IMPLIED
                  auto-complete   ( true |false ) "false"
                  identity        CDATA    #IMPLIED
                  access          ( read-only | shared | exclusive | db-locked )  "shared"
                  key-generator   IDREF    #IMPLIED >
        

The <class> element contains all the information used to map a Java object to a relational database. The content of <class> is mainly used to describe the fields that will be mapped.

Description of the attributes:

-name: The fully qualified package name of the Java object to map to.
-extends: Should be used _only_ if this Java object extends another Java object for which mapping information is provided. It should _not_ be used if the extended Java object is not referenced in the mapping file.
-depends: For more information on this field, please see Dependent and related relationships.
-identity: For more information on this field, please see Design -> Persistence.
-access: For more information on this field, please see Locking Modes.
-key-generator: For more information on this field, please see KeyGen.

Description of the elements:

-<description>: An optional description.
-<cache-type>: For more information on this field please see Bounded Dirty Checking and LRU Cache.
-<map-to>: Used to tell Castor the name of the relational table to which to map.
-<field>: Zero or more <field> elements are used to describe properties of each Java object.

The <map-to> element

        <!ELEMENT map-to EMPTY>
        <!ATTLIST map-to
                  table      NMTOKEN  #IMPLIED
                  xml        NMTOKEN  #IMPLIED
                  ns-uri     NMTOKEN  #IMPLIED
                  ns-prefix  NMTOKEN  #IMPLIED
                  ldap-dn    NMTOKEN  #IMPLIED
                  ldap-oc    NMTOKEN  #IMPLIED>
        

<map-to> is used to specify the name of the item that should be associated with the given Java object. The <map-to> element is only used for the root Java object.

Description of the attributes:

-table: The name of the relational database table to which the Java object is associated.

The <field> element

        <!ELEMENT field ( description?, sql?, xml?, ldap? )>
        <!ATTLIST field
                  name           NMTOKEN  #REQUIRED
                  type           NMTOKEN  #IMPLIED
                  required       ( true | false )  "false"
                  direct         ( true | false )  "false"
                  lazy           ( true | false )  "false"
                  transient      ( true | false )  "false"
                  get-method     NMTOKEN  #IMPLIED
                  set-method     NMTOKEN  #IMPLIED
                  create-method  NMTOKEN  #IMPLIED
                  collection     ( array | enumerate | collection | set | 
                                   arraylist | vector | map | hashtable )  #IMPLIED>
        

The <field> element is used to describe a property of a Java object. It provides:

-the identity ('name') of the property
-the type of the property (inferred from 'type' and 'collection')
-the access method of the property (inferred from 'direct', 'get-method', 'set-method')

From this information, Castor is able to access a given property in the Java object.

In order to determine the signature that Castor expects, there are two easy rules to apply.

1. Determine <type>.

-

If there is no 'collection' attribute, the object type is the value of the 'type' attribute. The value of the type attribute can be a fully qualified Java object like 'java.lang.String' or one of the allowed aliases:

short namePrimitive type?Java Class
big-decimalNjava.math.BigDecimal
big-integerYjava.math.BigInteger
booleanYjava.lang.Boolean.TYPE
byteYjava.lang.Byte.TYPE
bytesNbyte[]
charYjava.lang.Character.TYPE
charsNchar[]
clobNjava.sql.Clob
dateNjava.util.Date
doubleYjava.lang.Double.TYPE
floatYjava.lang.Float.TYPE
intYjava.lang.Integer.TYPE
integerYjava.lang.Integer
localeNjava.util.Locale
longYjava.lang.Long.TYPE
otherNjava.lang.Object
serializableYjava.io.Serializable
shortYjava.lang.Short.TYPE
sqldateYjava.sql.Date
sqltimeYjava.sql.Date
stringNjava.lang.String
stringsNString[]
streamNjava.io.InputStream
timestampNjava.sql.Timestamp

Castor will try to cast the data in the mapping file to the proper Java type.

-

If there is a collection attribute, the items in the following table can be used:

nametypedefault implementation
array<type_attribute>[]<type_attribute>[]
enumeratejava.util.Enumeration-
collectionjava.util.Collectionjava.util.ArrayList
setjava.util.Setjava.util.HashSet
arraylistjava.util.ArrayListjava.util.ArrayList
vectorjava.util.Vectorjava.util.Vector
mapjava.util.Mapjava.util.HashMap
hashtablejava.util.Hashtablejava.util.Hashtable

The type of the object inside the collection is the 'type' attribute. The 'default implementation' is the type used if the object holding the collection is found to be null and needs to be instantiated.

For hashtable and map, Castor will add an object using the put(object, object) method - the object is both the key and the value. This will be improved in the future.

It is necessary to use a collection when the content model of the element expects more than one element of the specified type. This is how the 'to-many' portion of a relationship is described.

2. Determine the signature of the method

  • If 'direct' is set to true, Castor expects to find a public Java object variable with the given signature:

            public <type> <name>;
            
  • If 'direct' is set to false or omitted, Castor will access the property though accessor methods. Castor determines the signature of the accessors and mutators as follows: If the 'get-method' or 'set-method' attributes are supplied, it will try to find a function with the following signature:

            public <type> <get-method>();
            
    or
            public void <set-method>(<type> value);
            

    If 'get-method' or 'set-method' attributes are not provided, Castor will try to find the following functions:

    	public void is<capitalized-name>()/public <type> get<capitalized-name>();

    the former for boolean types, the latter for all other types (or if the 'is<capitalized-name>()' method is not defined for a boolean type), and a standard set method of

    	public void set<capitalized-name>(<type> value);

    <capitalized-name> means that Castor uses the <name> attribute by changing its first letter to uppercase without modifying the other letters.

    The content of the <field> element will contain the information about how to map this field to the relational table.

    Description of the attributes:

    -name: If 'direct' access is used, 'name' should be the name of a public variable in the object we are mapping (the field must be public, not static and not transient). If no direct access and no 'get-/set-method' is specified, this name will be used to infer the name of the accessor and mutator methods.
    -type: The Java type of the field. This is used to access the field. Castor will use this information to cast the data type(e.g. string into integer). It is also used to define the signature of the accessor and mutator methods. If a collection is specified, this is used to specify the type of the object inside the collection. See description above for more details.
    -required: If true, the field is not optional.
    -direct: If true, Castor expects a public variable in the object and will modify it directly.
    -collection: If a parent object expects more than one occurrence of one of its fields, it is necessary to specify which collection type Castor will use to handle them. The type specified is used to define the type of the content inside the collection.
    -get-method: An optional name of the accessor method Castor should use. If this attribute is not set, Castor will try to guess the name with the algorithm described above.
    -set-method: An optional name of the mutator method Castor should use. If this attribute is not set, Castor will try to guess the name with the algorithm described above.
    -create-method: Factory method for instantiation of the object.

    The <sql> element

              <!ELEMENT sql EMPTY>
              <!ATTLIST sql
                  name        NMTOKENS  #IMPLIED
                  type        NMTOKENS  #IMPLIED
                  many-key    NMTOKENS  #IMPLIED
                  many-table  NMTOKEN   #IMPLIED
                  read-only   ( true | false )    "false"
                  dirty       ( check | ignore )  "check">
      
              

    The <sql> element is used to denote information about the database table to which a Java object is mapped. It should be declared for all <field> elements. Each <field> element contains one <sql> element. The <sql> element correlates directly to the <map-to> element for the containing <class> element. The <sql> elements contains the following attributes:

    -name: The name of the column in the database table.
    -type: The JDBC type of the column. It is inferred from the object when the type of this field is a persistent Java class that is defined elsewhere in the mapping.
    -read-only: If true, the column in the relational database table will only be read, not updated or deleted.
    -dirty: If the value is 'ignore', the field will not be checked against the database for modification.

    There are two more attributes used only with 'to-many' relations.

    -many-key: Specifies the name of the column that holds the foreign key to this object. That column is in the database table that stores objects of the Java type of this field.
    -many-table: Specifies the name of the bridge table that contains the primary keys of the object on each side of the relationship. This is only used for many-to-many relationships.

    Examples

    Here are examples of object mappings and the corresponding Java objects and DDL for the database table.

    Java class files

    The following fragment shows the Java class declaration for the Product class:

    package myapp;
    
    public class Product 
    {
        private int       _id;
    
        private String    _name; 
    
        private float     _price; 
    
        private ProductGroup _group;
    
    
        public int getId() { ... }
    
        public void setId( int anId ) { ... }
    
        public String getName() { ... }
    
        public void setName( String aName ) { ... }
    
        public float getPrice() { ... }
    
        public void setPrice( float aPrice ) { ... }
    
        public ProductGroup getProductGroup() { ... }
    
        public void setProductGroup( ProductGroup aProductGroup ) { ... }
    }
                   

    The following fragment shows the Java class declaration for the ProductGroup class:

    
    
    public class ProductGroup
    {
    
        private int       _id;
    
        private String    _name;
    
        public int getId() { ... }
    
        public void setId( int id ) { ... }
    
        public String getName() { ... }
    
        public void setName( String name ) { ... }
    
    }
                      

    DDL

    The following sections show the DDL for the relational database tables PROD, PROD_GROUP and PROD_DETAIL:

    PROD:

    create table prod 
    (
      id        int           not null,
      name      varchar(200)  not null,
      price     numeric(18,2) not null,
      group_id  int           not null
    );
                       

    PROD_GROUP:

    create table prod_group (
      id        int           not null,
      name      varchar(200)  not null
    );
                       

    PROD_DETAIL:

    create table prod_detail (
      id        int           not null,
      prod_id   int           not null,
      name      varchar(200)  not null
    );
                       

    Object Mappings

    The following code fragment shows the object mapping for the ProductGroup class:

    <?xml version="1.0"?>
    <!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Object Mapping DTD Version 1.0//EN"
                             "http://castor.exolab.org/mapping.dtd">
    <mapping>
    
      <class name="myapp.ProductGroup" identity="id">
      
        <description>Product group</description>
        
        <map-to table="prod_group" xml="group" />
        
        <field name="id" type="integer" >
          <sql name="id" type="integer"/>
        </field>
        
        <field name="name" type="string">
          <sql name="name" type="char" />
        </field>
        
      </class>
    
    </mapping>
                        

    As a result of that declaration, Castor JDO would create the following SQL statements for creating, deleting, loading and updating instances of ProductGroup:

    create: INSERT INTO prod_group (id, name) VALUES (?,?)
    delete: DELETE FROM prod_group WHERE id=?
    load:   SELECT prod_group.id, prod_group.name FROM prod_group WHERE prod_group.id=?;
    update: UPDATE prod_group SET name=? WHERE id=?
                 

    Read-only fields

    To declare the name field read-only, above field definition for the field name needs to be changed to:

                 
      <class name="myapp.ProductGroup" identity="id">
      
        ...    
        <field name="name" type="string">
          <sql name="name" type="char" read-only="true" />
        </field>
        
      </class>
                 

    As a result of that declaration, Castor JDO creates the following SQL statements for creating, deleting, loading and updating instances of ProductGroup:

     
                 
    create: INSERT INTO prod_group (id) VALUES (?)
    delete: DELETE FROM prod_group WHERE id=?
    load:   SELECT prod_group.id, prod_group.name FROM prod_group WHERE prod_group.id=?;
    update: no statement will be generated
                 

    1:1 relation

    The following code fragment shows the mapping file for the Product class. Apart from the simple field declarations, this includes a simple 1:1 relation between Product and ProductGroup, where every product instance is associated with a ProductGroup:

                 
        ...
        <class name="myapp.Product" identity="id">
    
            <map-to table="prod" />
    
            <field name="id" type="integer">
                <sql name="id" type="integer" />
            </field>
    
            <field name="name" type="string">
                <sql name="name" type="char" />
            </field>
    
            <field name="price" type="float">
                <sql name="price" type="numeric" />
            </field>
    
            <field name="group" type="myapp.ProductGroup" >
                <sql name="group_id" />
            </field>
            
            <field name="details" type="myapp.ProductDetail"
                   collection="vector">
               <sql many-key="prod_id"/>
            </field>
            
            <field name="categories" type="myapp.Category" collection="vector">
               <sql name="category_id"
                    many-table="category_prod" many-key="prod_id" />
            </field>
    
        </class>
        ...
                 

    1:M relation

    The following code fragment shows (again) the mapping file for the Product class. The field definition highlighted shows how to declare a 1:M relation between Product and ProductDetail, where every product instance is made up of many ProductDetails:

                    
        ...
        <class name="myapp.Product" identity="id">
    
            <map-to table="prod" />
    
            <field name="id" type="integer">
                <sql name="id" type="integer" />
            </field>
    
            <field name="name" type="string">
                <sql name="name" type="char" />
            </field>
    
            <field name="price" type="float">
                <sql name="price" type="numeric" />
            </field>
    
            <field name="group" type="myapp.ProductGroup" >
                <sql name="group_id" />
            </field>
            
            <field name="details" type="myapp.ProductDetail" collection="vector">
               <sql many-key="prod_id"/>
            </field>
    
            <field name="categories" type="myapp.Category" collection="vector">
               <sql name="category_id"
                    many-table="category_prod" many-key="prod_id" />
            </field>
        </class>
        ...
                    

    The following code fragment shows the corresponding mapping entry for the ProductDetail class that defines the second leg of the 1:M relation between Product and ProductDetail.

                    
      ...
      <class name="myapp.ProductDetail" identity="id" depends="myapp.Product" >
    
        <description>Product detail</description>
    
        <map-to table="prod_detail" xml="detail" />
    
        <field name="id" type="integer">
          <sql name="id" type="integer"/>
        </field>
    
        <field name="name" type="string">
          <sql name="name" type="char"/>
        </field>
    
        <field name="product" type="myapp.Product">
          <sql name="prod_id" />
        </field>
    
      %lt;/class>
      ...
                    

    M:N relation

    The following code fragment shows (again) the mapping file for the Product class. The field definition highlighted shows how to declare a M:N relation between Product and ProductCategory, where many products can be mapped to many product categories:

                    
        ...
        <class name="myapp.Product" identity="id">
    
            <map-to table="prod" />
    
            <field name="id" type="integer">
                <sql name="id" type="integer" />
            </field>
    
            <field name="name" type="string">
                <sql name="name" type="char" />
            </field>
    
            <field name="price" type="float">
                <sql name="price" type="numeric" />
            </field>
    
            <field name="group" type="myapp.ProductGroup" >
                <sql name="group_id" />
            </field>
            
            <field name="details" type="myapp.ProductDetail" collection="vector">
               <sql many-key="prod_id">/>
            </field>
    
            <field name="categories" type="myapp.Category" collection="vector">
               <sql name="category_id"
                    many-table="category_prod" many-key="prod_id" />
            </field>
            
        </class>
        ...
                    

    The following code fragment shows the corresponding mapping entry for the ProductCategory class that defines the second leg of the M:N relation between Product and Category.

                    
      ...
      <class name="myapp.Category" identity="id">
      
        <description>
            A product category, any number of products can belong to
            the same category, a product can belong to any number of
            categories
        </description>
        
        <map-to table="category" xml="category" />
        
        <field name="id" type="integer">
          <sql name="id" type="integer"/>
        </field>
        
        <field name="name" type="string">
          <sql name="name" type="char"/>
        </field>
        
        <field name="products" type="myapp.Product" collection="vector">
          <sql name="prod_id"
               many-table="category_prod" many-key="category_id" />
        </field>
        
      </class>
      ...
                    

  •  
       
      
       
     


    Copyright ? 1999-2005 ExoLab Group, Intalio Inc., and Contributors. All rights reserved.
     
    Java, EJB, JDBC, JNDI, JTA, Sun, Sun Microsystems are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and in other countries. XML, XML Schema, XSLT and related standards are trademarks or registered trademarks of MIT, INRIA, Keio or others, and a product of the World Wide Web Consortium. All other product names mentioned herein are trademarks of their respective owners.