7.6. Class Mapping

7.6.1. Base Mapping
7.6.2. Flat Inheritance Mapping
7.6.2.1. Advantages of using Flat Inheritance Mapping
7.6.2.2. Disadvantages of using Flat Inheritance Mapping
7.6.3. Vertical Inheritance Mapping
7.6.3.1. Advantages of using Vertical Inheritance Mapping
7.6.3.2. Disadvantages of using Vertical Inheritance Mapping
7.6.3.3. Vertical Select Modes
7.6.4. Horizontal Inheritance Mapping
7.6.4.1. Special considerations when using Horizontal Inheritance Mapping
7.6.4.2. Advantages of using Horizontal Inheritance Mapping
7.6.4.3. Disadvantages of using Horizontal Inheritance Mapping
7.6.5. Custom Class Mapping

A class mapping describes how a class maps to the database. It typically controls the primary table for the class and how the class is linked to its superclass data, if any. For classes using datastore identity, the class mapping also manages the primary key column for the class.

In Kodo JDO, class mappings extend the base kodo.jdbc.meta.ClassMapping class. The concrete class mappings Kodo JDO provides are described in the sections below. By default, the mapping tool uses the base class mapping for all persistent classes without a persistence-capable superclass, and the flat class mapping for all persistent subclasses. You can change the default subclass mapping type with the kodo.jdbc.SubclassMapping configuration property. You can also instruct the mapping tool to use a specific mapping for an individual class with the jdbc-class-map-name JDO metadata extension.

7.6.1. Base Mapping

The base class mapping is reserved for persistent classes that do not extend from any other persistent class. The base class mapping has the following attributes:

  • type: base

  • table: The name of the table in which the primary key data is stored for each record of this class. This property is required.

  • pk-column: The name of the primary key column for classes that use datastore identity. This property is not used for classes with application identity. The named column must be of some numeric type, as Kodo JDO uses Java long values for datastore identities.

Example 7.16. Using a Base Mapping

Java class:

public class Magazine
{
    ... class content ...
}


Schema: 

<table name="MAGAZINE">
    <column name="JDOID" type="bigint"/>
    <pk column="JDOID"/>
    ... columns for magazine fields ...
</table> 


JDO metadata:

<class name="Magazine">
    ... field metadata ...
</class>


Mapping information using the mapping XML format:

<class name="Magazine">
    <jdbc-class-map type="base" table="MAGAZINE" pk-column="JDOID"/>
    ... indicator mappings ...
    ... field mappings     ...
</class>


Mapping information using JDO metadata extensions:

<class name="Magazine">
    <extension vendor-name="kodo" key="jdbc-class-map" value="base">
        <extension vendor-name="kodo" key="table" value="MAGAZINE"/>
        <extension vendor-name="kodo" key="pk-column" value="JDOID"/>
    </extension>
    ... indicator extensions ...
    ... field metadata       ...
</class>

7.6.2. Flat Inheritance Mapping

The flat inheritance mapping (sometimes referred to as single-table or filtered inheritance) is a mapping for persistent subclasses that stores their fields in the same table as the parent class.

The flat class mapping has the following attributes:

  • type: flat

Example 7.17. Using a Flat Mapping

Java class:

public class Tabloid
    extends Magazine
{
    ... class content ...
}


Schema: 

<table name="MAGAZINE">
    ... primary key columns         ...
    ... columns for magazine fields ...
    ... columns for tabloid fields  ...
</table> 


JDO metadata:

<class name="Tabloid" persistence-capable-superclass="Magazine">
    ... field metadata ...
</class>


Mapping information using the mapping XML format:

<class name="Tabloid">
    <jdbc-class-map type="flat"/>
    ... indicator mappings ...
    ... field mappings     ...
</class>


Mapping information using JDO metadata extensions:

<class name="Tabloid" persistence-capable-superclass="Magazine">
    <extension vendor-name="kodo" key="jdbc-class-map" value="flat"/>
    ... indicator extensions ...
    ... field metadata       ...
</class>

7.6.2.1. Advantages of using Flat Inheritance Mapping

Flat inheritance mapping is the fastest of all inheritance models, since it never requires a join to retrieve a single persistent instance from the database. Similarly, persisting or updating a single persistent instance requires only a single INSERT or UPDATE statement (unlike vertical inheritance mapping).

7.6.2.2. Disadvantages of using Flat Inheritance Mapping

The larger the inheritance model gets, the "wider" the mapped table gets, in that for every field in the entire inheritance hierarchy, a column must exist in the mapped table. This may have undesirable consequence on the database size, since a deep inheritance hierarchy will result in tables with many columns.

7.6.3. Vertical Inheritance Mapping

Subclasses whose derived fields are in a different table than their superclass fields use a vertical class mapping.

The vertical class mapping has the following attributes:

  • type: vertical

  • table: The name of the table in which the derived fields of the class are stored. This property is required.

  • ref-column.<pk column>*: Kodo JDO must be able to join this class' table to the table of the parent class. Each ref-column attribute joins a column in this class' table to the corresponding column in the parent class' table. See the previous discussion of join attributes for details on joins.

  • ref-constant.<column>*: Similar to the ref-column attribute, but used when the join relies on a column in the joined-to table having a constant value. For more information on constant joins, see Section 7.5.2, “Non-Standard Joins”.

Example 7.18. Using a Vertical Mapping

Java class:

public class Tabloid
    extends Magazine
{
    ... class content ...
}


Schema: 

<table name="MAGAZINE">
    <column name="JDOID" type="bigint"/>
    <pk column="JDOID"/>
    ... columns for magazine fields ...
</table> 

<table name="TABLOID">
    <column name="MAG_ID" type="bigint"/>
    <fk to-table="MAGAZINE">
        <join column="MAG_ID" to-column="JDOID"/>
    </fk>
    ... columns for tabloid fields ...
</table> 


JDO metadata:

<class name="Tabloid" persistence-capable-superclass="Magazine">
    ... field metadata ...
</class>


Mapping information using the mapping XML format:

<class name="Tabloid">
    <jdbc-class-map type="vertical" table="TABLOID" ref-column.JDOID="MAG_ID"/>
    ... indicator mappings ...
    ... field mappings     ...
</class>


Mapping information using JDO metadata extensions:

<class name="Tabloid" persistence-capable-superclass="Magazine">
    <extension vendor-name="kodo" key="jdbc-class-map" value="vertical">
        <extension vendor-name="kodo" key="table" value="TABLOID"/>
        <extension vendor-name="kodo" key="ref-column.JDOID" value="MAG_ID"/>
    </extension>
    ... indicator extensions ...
    ... field metadata       ...
</class>

7.6.3.1. Advantages of using Vertical Inheritance Mapping

Vertical inheritance mapping results in the most "normalized" database schema, since spurious and redundant data will not exist in any of the tables. As more subclasses are added to the data model over time, the only schema modification that needs to be made is the addition of a corresponding table in the database (rather than having to change the structure of existing tables).

7.6.3.2. Disadvantages of using Vertical Inheritance Mapping

Vertical mappings can be the slowest of all the inheritance models, since retrieving any subclass will require one or more database JOINs, and storing subclasses will require multiple INSERT or UPDATE statements. This is only the case when persistence operations are performed on subclasses; if most operations are performed on the topmost persistent superclass, then a vertical mapping will be as fast as a flat mapping.

7.6.3.3. Vertical Select Modes

When executing a select against a hierarchy that uses vertical inheritance, you must consider how to load subclass data. Section 14.2, “Eager Fetching” describes your options.

7.6.4. Horizontal Inheritance Mapping

The horizontal inheritance mapping (sometimes referred to as distributed or one-table-per-leaf inheritance) is a mapping that maps each concrete class in an inheritance hierarchy to a separate table. A horizontally mapped class does not itself have a table; all the fields in the class will be mapped by the subclasses. Therefore, a horizontally mapped class cannot be directly persisted, only subclasses of the class can be stored in the database. For this reason, it is recommended (but not required) that horizontally mapped classes be declared abstract.

When defining mappings for horizontal subclasses, the field mappings of the superclass' fields are defined in the mapping definition for the subclass. When the mappings are defined in the .mapping format, field mappings for superclasses will be named "package.name.to.SuperClass.fieldName", or, if the superclass is in the same package as the subclass that defines its field, this can be shortened to: "SuperClass.fieldName".

When defining the mappings for a horizontal superclass in a .jdo metadata file, the field mapping definitions must be contained in a special "jdbc-field-mappings" extension element, since the .jdo format disallows <field> tags to be specified for field names that are not declared by the owning class.

As well as having support for a most-derived horizontal mapping, Kodo can also have intermediate classes in any inheritance hierarchy (either vertical or flat) be declared as horizontal. This is convenient for abstract persistent classes for which no table is to be defined.

Example 7.19. Using a Horizontal Mapping

Java class:

public class Magazine
{
    private String title;
    ... class content ...
}


JDO metadata:

<class name="Magazine">
    ... field metadata ...
</class>


Mapping information using the mapping XML format:

<class name="Magazine">
    <jdbc-class-map type="horizontal"/>
</class>


Mapping information using JDO metadata extensions:

<class name="Magazine">
    <extension vendor-name="kodo" key="jdbc-class-map" value="horizontal"/>
    ... field metadata ...
</class>

Java class:

public class Tabloid
    extends Magazine
{
    private String tabloidType;
    ... class content ...
}


Schema: 

<table name="TABLOID">
    <pk column="JDOID"/>
    <column name="JDOID" type="bigint"/>
    <column name="TABLOID_TYPE" type="varchar" size="255"/>
    <column name="TITLE" type="varchar" size="255"/>
    ... columns for other tabloid fields ...
</table> 


JDO metadata:

<class name="Tabloid" persistence-capable-superclass="Magazine">
    ... field metadata ...
</class>


Mapping information using the mapping XML format:

<class name="Tabloid">
    <jdbc-class-map type="base" table="TABLOID"/>
    ... indicator mappings ...
    <field name="tabloidType">
        <jdbc-field-map type="value" column="TABLOID_TYPE"/>
    </field>
    <field name="Magazine.title">
        <jdbc-field-map type="value" column="TITLE"/>
    </field>
    ... other field mappings ...
</class>


Mapping information using JDO metadata extensions:

<class name="Tabloid" persistence-capable-superclass="Magazine">
    <extension vendor-name="kodo" key="jdbc-class-map" value="base">
        <extension vendor-name="kodo" key="table" value="TABLOID"/>
    </extension>
    ... indicator extensions ...
    <field name="tabloidType">
        <extension vendor-name="kodo" key="jdbc-field-map" value="value">
            <extension vendor-name="kodo" key="column" value="TABLOID_TYPE"/>
        </extension>
    </field>
    <extension vendor-name="kodo" key="jdbc-field-mappings">
        <extension vendor-name="kodo" key="Magazine.title">
            <extension vendor-name="kodo" key="jdbc-field-map" value="value">
                <extension vendor-name="kodo" key="column" value="TITLE"/>
            </extension>
        </extension>
    </extension>
</class>

Example 7.20. Using a Horizontal Mapping with Application Identity hierarchy

Java class:

public class Magazine
{
    private int id;
    private String title;
    ... class content ...


    // application identity class for Manager; represented as
    // a static inner class for the Managzine class
    public static class MagazineID
    {
        public int id;


        public MagazineID ()
        {
        }


        public MagazineID (String idString)
        {
            id = new Integer (idString).intValue ();
        }


        public int hashCode ()
        {
            return id;
        }


        public boolean equals (Object other)
        {
            if (other == null || other.getClass () != getClass ())
                return false;

            return ((MagazineID)other).id == id;
        }
    }
}


JDO metadata:

<class name="Magazine" objectid-class="Magazine$MagazineID">
    <field name="id" primary-key="true"/>
    ... field metadata ...
</class>


Mapping information using the mapping XML format:

<class name="Magazine">
    <jdbc-class-map type="horizontal"/>
</class>


Mapping information using JDO metadata extensions:

<class name="Magazine" objectid-class="Magazine$MagazineID">
    <extension vendor-name="kodo" key="jdbc-class-map" value="horizontal"/>
    <field name="id" primary-key="true"/>
    ... field metadata ...
</class>

Java class:

public class Tabloid
    extends Magazine
{
    private String tabloidType;
    ... class content ...


    public static class TabloidID
        extends Magazine.MagazineID
    {
        public TabloidID ()
        {
            super ();
        }


        public TabloidID (String idString)
        {
            super (idString);
        }

        // note that equals() and hashCode() methods in superclass
        // are sufficient for application identity hierarchy
    }
}


Schema: 

<table name="TABLOID">
    <pk column="ID"/>
    <column name="ID" type="bigint"/>
    <column name="TABLOID_TYPE" type="varchar" size="255"/>
    <column name="TITLE" type="varchar" size="255"/>
    ... columns for other tabloid fields ...
</table> 


JDO metadata:

<class name="Tabloid" persistence-capable-superclass="Magazine"
    objectid-class="Tabloid$TabloidID">
    ... field metadata ...
</class>


Mapping information using the mapping XML format:

<class name="Tabloid">
    <jdbc-class-map type="base" table="TABLOID"/>
    ... indicator mappings ...
    <field name="Magazine.id">
        <jdbc-field-map type="value" column="ID"/>
    </field>
    <field name="tabloidType">
        <jdbc-field-map type="value" column="TABLOID_TYPE"/>
    </field>
    <field name="Magazine.title">
        <jdbc-field-map type="value" column="TITLE"/>
    </field>
    ... other field mappings ...
</class>


Mapping information using JDO metadata extensions:

<class name="Tabloid" persistence-capable-superclass="Magazine"
    objectid-class="Tabloid$TabloidID">
    <extension vendor-name="kodo" key="jdbc-class-map" value="base">
        <extension vendor-name="kodo" key="table" value="TABLOID"/>
    </extension>
    ... indicator extensions ...
    <field name="tabloidType">
        <extension vendor-name="kodo" key="jdbc-field-map" value="value">
            <extension vendor-name="kodo" key="column" value="TABLOID_TYPE"/>
        </extension>
    </field>
    <extension vendor-name="kodo" key="jdbc-field-mappings">
        <extension vendor-name="kodo" key="Magazine.id">
            <extension vendor-name="kodo" key="jdbc-field-map" value="value">
                <extension vendor-name="kodo" key="column" value="ID"/>
            </extension>
        </extension>
        <extension vendor-name="kodo" key="Magazine.title">
            <extension vendor-name="kodo" key="jdbc-field-map" value="value">
                <extension vendor-name="kodo" key="column" value="TITLE"/>
            </extension>
        </extension>
    </extension>
</class>

7.6.4.1. Special considerations when using Horizontal Inheritance Mapping

  • Declaring classes abstract: Kodo does not require that a class that is horizontally mapped be declared abstract. However, it is recommended that these classes not be concrete, since any attempt to persist a superclass that is horizontally mapped will throw a FatalUserException.

  • Application identity: When a topmost horizontally-mapped superclass uses application identity, one of the following two restrictions must be observed by the application developer:

    1. The primary key values in each of the tables that extend the horizontally mapped superclass much be unique. That is, if MySuperClass declares horizontal mapping, and MySubClass1 and MySubClass2 extend MySuperClass and are mapped to "MY_SUB1" and "MY_SUB2" (respectively), then there can be no row in MY_SUB1 that has a primary key value that exists in MY_SUB2. This is because a call to getObjectById with an application identity instance cannot identify which subclass the ID is associated with.

    2. Rather than having a single application identity class associated with the entire class hierarchy, each subclass declares its own application identity class. The inheritance hierarchy of the application identity classes must exactly match the inheritance hierarchy of the persistent classes they represent. This enables Kodo to unambiguously identify which subclass a specific application identity instance represents.

  • Relations: Relations to horizontally mapped classes will be treated the same as relations to interfaces or fields of type PersistenceCapable. That is, the relations will be stored as the stringified object id.

7.6.4.2. Advantages of using Horizontal Inheritance Mapping

An advantage of using a horizontal mapping is that attributes that are common to multiple persistent classes can be defined in the superclass without having to suffer the performance consequences and relational design restrictions of using a vertical mapping. Persisting and modifying instances of subclasses whose superclass uses horizontal mapping is as fast as flat inheritance mapping, since typically only a single INSERT or UPDATE statement will need to be issued.

7.6.4.3. Disadvantages of using Horizontal Inheritance Mapping

In addition to the restrictions imposed by using horizontal mappings (see Section 7.6.4.1, “Special considerations when using Horizontal Inheritance Mapping”), queries against the superclass fields of a horizontally-mapped class will require multiple SELECT statements to be executed against the database (one for each concrete subclass). Furthermore, from a relational design standpoint, horizontal mappings are not normalized, since attributes are repeated across different tables.

7.6.5. Custom Class Mapping

Kodo JDO allows you to create your own class mappings. A custom class mapping can override any or all of the CRUD operations for objects of the class: Create, Retrieve, Update, Delete. All mappings must extend, directly or indirectly, from kodo.jdbc.meta.ClassMapping.

The jdbc-class-map-name JDO metadata extension tells the mapping tool which class mapping to install. If you write mappings by hand rather than with the mapping tool, simply specify the full class name of your custom mapping in the type attribute of the mapping XML.

The samples/ormapping directory of the Kodo JDO distribution includes examples of custom mappings.

[Note]Note

Kodo's custom class mapping capabilities require a Kodo JDO Enterprise Edition license in order to function. See Chapter 13, Enterprise Edition for details about the Enterprise Edition.