Chapter 7. Object-Relational Mapping

7.1. Mapping Tool
7.1.1. Using the Mapping Tool
7.1.2. Generating DDL SQL
7.2. Automatic Runtime Mapping
7.3. Mapping Factory
7.3.1. Importing and Exporting Mapping Data
7.4. Mapping File XML Format
7.5. Mapping Notes
7.5.1. Join Attributes
7.5.2. Non-Standard Joins
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 Query 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
7.7. Version Indicator
7.7.1. Version Number Indicator
7.7.2. Version Date Indicator
7.7.3. State Image Indicator
7.7.4. Custom Version Indicator
7.8. Class Indicator
7.8.1. In-Class-Name Indicator
7.8.2. Metadata Value Indicator
7.8.3. Custom Class Indicator
7.9. Field Mapping
7.9.1. Value Mapping
7.9.2. Blob Mapping
7.9.3. Clob Mapping
7.9.4. Byte Array Mapping
7.9.5. One-to-One Mapping
7.9.6. PC One-to-One Mapping
7.9.7. Embedded One-to-One Mapping
7.9.8. Collection Mapping
7.9.9. Many-to-Many Mapping
7.9.10. One-to-Many Mapping
7.9.11. PC Collection Mapping
7.9.12. Map Mapping
7.9.13. N-to-Many Map Mapping
7.9.14. Many-to-N Map Mapping
7.9.15. Many-to-Many Map Mapping
7.9.16. PC Map Mapping
7.9.17. N-to-PC Map Mapping
7.9.18. PC-to-N Map Mapping
7.9.19. PC-to-Many Map Mapping
7.9.20. Many-to-PC Map Mapping
7.9.21. Custom Field Mapping
7.9.22. Externalization

Object-relational mapping is the process of mapping software objects to relational database tables. Kodo JDO has a full-featured mapping system including built-in support for most object-oriented patterns and schema designs. You are not limited to what Kodo JDO bundles by default, however. Kodo JDO's mapping system is designed with flexibility and extensibility in mind, so you can easily create custom mappings to meet your exact needs. This chapter reviews the mapping utilities Kodo JDO provides, the built-in mappings it supports, and how to create your own mappings should the need arise.

7.1. Mapping Tool

Kodo JDO allows you to control the mapping process yourself, but it also provides tools to automate mapping. We already saw one example of Kodo JDO's object-relational mapping tools when we discussed the reverse mapping tool. In this section, we discuss another mapping utility, simply called the mapping tool. While the reverse mapping tool creates classes and mapping data from an existing schema, the mapping tool creates the schema and mapping data from existing classes. The mapping tool can also be used to validate mapping data that you've written yourself, or to import and export mapping data to and from the current mapping factory.

We describe common mapping tool use cases in the next section. You can invoke the mapping tool through the mappingtool shell/batch script included in the Kodo JDO distribution, or through its Java class, kodo.jdbc.meta.MappingTool.

Example 7.1. Using the Mapping Tool

mappingtool -a refresh *.jdo

In addition to the universal flags of the configuration framework, the mapping tool accepts the following command line arguments:

  • -file/-f <stdout | output file>: Use this option to write the planned mappings to an XML document rather than recording them as the mappings for the given classes. This option also specifies the file to dump to if using the export tool action.

  • -schemaAction/-sa <add | refresh | retain | none> : The action to take on the schema. These options correspond to the same-named actions on the schema tool described in Section 8.2, “Schema Tool”. Unless you are running the mapping tool on all of your persistent types at once, we strongly recommend you use the default add schema action. Otherwise you may end up inadvertently dropping schema components that are used by classes you are not currently running the tool over.

  • -schemaFile/-sf <stdout | output file>: Use this option to write the planned schema to an XML document rather than modify the data store. The document can then be manipulated and committed to the database with the schema tool.

  • -dropTables/-dt <true/t | false/f>: Corresponds to the same-named option on the schema tool.

  • -ignoreErrors/-i <true/t | false/f>: Corresponds to the same-named option on the schema tool.

  • -schemas/-s <schema and table names>: Corresponds to the same-named option on the schema tool. This option is ignored if readSchema is not set to true.

  • -readSchema/-rs <true/t | false/f>: Set this option to true to read the entire existing schema when the tool runs. Reading the existing schema ensures that Kodo does not generate any mappings that use table, index, primary key, or foreign key names that conflict with existing names. Depending on the JDBC driver, though, it can be a very slow process for large schemas.

  • -primaryKeys/-pk <true/t | false/f>: Whether to read and manipulate primary key information of existing tables. Defaults to false unless the readSchema flag is set to true.

  • -foreignKeys/-fk <true/t | false/f>: Whether to read and manipulate foreign key information of existing tables. Defaults to false unless the readSchema flag is set to true. This means that if you add a jdbc-delete-action extension to a field of a class that has already been mapped once, you must explicitly set this flag to true to have Kodo create the new foreign key on the existing table.

  • -indexes/-ix <true/t | false/f>: Whether to read and manipulate index information of existing tables. Defaults to false unless the readSchema flag is set to true. This means that if you add a jdbc-indexed extension to a field of a class that has already been mapped once, you must explicitly set this flag to true to have Kodo create the new index on the existing table.

The mapping tool requires an -action/-a argument specifying the action to take on each class. The available actions are:

  • refresh: Bring the mapping information up-to-date with the class definitions. Classes or fields whose mappings no longer match the class definition or schema will be re-mapped to new columns/tables.

  • drop: Remove the mapping information for the given classes.

  • validate: Validate that the mappings for the given classes are valid and that they match the schema. No mappings or tables will be changed; an exception will be thrown if any mappings are invalid.

  • buildSchema: Create the schema based on the existing mappings for the given classes.

  • revert: Revert the mappings for the given classes to their previously saved state. Some mapping factories may not be able to revert mapping data.

  • import: Import mapping information from the given XML document and add it to the stored system mappings. The XML format used for mapping data is discussed in Section 7.4, “Mapping File XML Format”.

  • export: Export the mapping data for the given classes to an XML file. The XML format used for mapping data is discussed in Section 7.4, “Mapping File XML Format”.

Each additional argument to the tool should be either the full name of a persistent class, the .java file of a persistent class, the .class file of a persistent class, or a .jdo metadata file listing one or more persistent classes to act on. If the import action is used, however, then any additional arguments will be interpreted as XML mapping data files.

The mapping data generated by the mapping tool is stored in the system mapping factory. As you will see later in this chapter, you have several mapping factories to choose from. Thus, mapping data might end up stored in the database, in special mapping files, in JDO metadata vendor extensions, or in another format of your choosing.

7.1.1. Using the Mapping Tool

There are three primary approaches to object-relational mapping: object-to-schema, schema-to-object, and meet-in-the-middle. The mapping tool has actions to facilitate each approach.

In the object-to-schema approach to mapping, you concentrate your efforts on your object model, and the mapping tool's refresh action keeps your mappings and schema up-to-date. The refresh action examines both the existing database schema and any existing mapping information. Classes and fields that are not mapped, or whose mapping information no longer matches the object model or the schema, are automatically given new mappings. The tool also updates the schema as necessary to support these new mappings. The example below shows how to invoke the refresh action on the mapping tool to create or update the mapping information and database schema for the persistent classes listed in package.jdo.

Example 7.2. Refreshing Mappings and the Relational Schema

mappingtool -a refresh package.jdo

You can safely run the refresh action on classes that have already been mapped, because the tool only generates new mappings when the old ones have become incompatible with the class or the schema. If the tool does have to replace a bad mapping, it does not modify other still-valid mappings. For example, if you change the type of a field from int to String, the mapping tool will detect the incompatibility with the old numeric column, add a new string-compatible column to the class' database table, and change the field's mapping data to point to the new column. All other fields will retain their original mappings.

To drop mapping data, use the drop action. This action does not affect the schema. Dropping mapping data for unused classes is not strictly necessary, but it might slightly increase performance under some mapping factories.

Example 7.3. Dropping Mappings

mappingtool -a drop package.jdo

The second approach to object-relational mapping is the schema-to-object approach. We have already seen how to use the reverse mapping tool to generate persistent classes and mapping information from an existing schema. Once you complete the reverse mapping, you may want to tweak the output of the reverse mapping tool. At this point you have both an existing schema and existing mapping information (from the reverse mapping tool), and you are modifying both by hand. Thus, you are really using the final, meet-in-the-middle approach to mapping.

In the meet-in-the-middle mapping approach, you control both the relational model and the object model. It is up to you to define the mappings between these models, possibly with the aid of Kodo JDO's GUI tools. In this scenario, you will find the mapping tool's validate action useful. The validate action verifies that the mapping information for a class matches the class definition and the existing schema.

Example 7.4. Validating Mappings

mappingtool -a validate package.jdo

The buildSchema tool action is also useful for meet-in-the-middle mapping. Unlike the validate action, which throws an exception if the mapping data does not match the existing schema, or the refresh action, which replaces inconsistent mappings, the buildSchema action assumes your mapping data is correct, and modifies the schema to match your mappings. This lets you modify your mapping data manually, but saves you the hassle of using your database's tools to bring the schema up-to-date.

Example 7.5. Updating the Schema Based on Mapping Data

mappingtool -a buildSchema package.jdo

The buildSchema action is also useful if you would like Kodo to do most of your mappings, but you want to edit a few of the mappings or table/column names Kodo generates:

Example 7.6. Modifying Default Mappings

First, run the mapping tool with a none schema action to generate default mappings without changing the database:

mappingtool -a refresh -sa none package.jdo

Next, modify the default mappings to fit your needs. You only have to do this once; Kodo will continue to use the modified mappings as long as they remain valid. Finally, build the schema based on your mappings:

mappingtool -a buildSchema package.jdo

Finally, some mapping factories allow you to revert mapping data if they have saved a copy.

Example 7.7. Reverting Mapping Data

mappingtool -a revert package.jdo

7.1.2. Generating DDL SQL

Sometimes you do not want Kodo to make the changes to your database, but rather you want to have a script of the SQL that Kodo would use. In order to do this, you need to use mapping tool with schema tool (see Section 8.2, “Schema Tool”). This involves first writing the planned schema to an XML document (e.g. schema.xml) rather than modifying the data store. Then you need to ask the schema tool to create either the full DDL for the database, or just the DDL to bring the database up to date with your mappings. Note that this process can be automated using Ant (see Section 15.2, “Apache Ant”).

Example 7.8. Refresh Mappings and Create DDL

mappingtool -a refresh -sf schema.xml package.jdo
schematool -a build -f script.sql schema.xml

Example 7.9. Refresh Mappings and Create DDL to Update Database

mappingtool -a refresh -sf schema.xml package.jdo
schematool -a add -f script.sql schema.xml

Example 7.10. Create DDL for Current Mappings

mappingtool -a buildSchema -sf schema.xml package.jdo
schematool -a build -f script.sql schema.xml

Example 7.11. Create DDL to Update Database for Current Mappings

mappingtool -a buildSchema -sf schema.xml package.jdo
schematool -a add -f script.sql schema.xml

Example 7.12. Ant Target to Create DDL for Current Mappings

<target name="buildDDL">

    <!-- Define the mappingtool and schematool tasks; this can be    -->
    <!-- done at the top of the build.xml file, so it will be        -->
    <!-- available for all targets.                                  -->
    <taskdef name="mappingtool" classname="kodo.jdbc.ant.MappingToolTask"/>
    <taskdef name="schematool" classname="kodo.jdbc.ant.SchemaToolTask"/>

    <!-- Build a schema XML file for all .jdo files below the        -->
    <!-- current directory.  If we want to refresh the mappings at   -->
    <!-- the same time, we can use the "refresh" action instead.     -->
    <mappingtool action="buildSchema" schemaFile="${schemafile}">
        <fileset dir=".">
            <include name="**/*.jdo" />
        </fileset>
    </mappingtool>

    <!-- Build a DDL file that will add all the tables defined in    -->
    <!-- the schema file created above.  If we want the DDL to only  -->
    <!-- contain updates to the existing schema, we can use the      -->
    <!-- "add" action instead.                                       -->
    <schematool action="build" file="${ddlfile}">
        <fileset dir=".">
            <include name="${schemafile}" />
        </fileset>
    </schematool>
</target>