3.3. Auto-Generating Classes from a Schema

3.3.1. Customizing Reverse Mapping
3.3.2. Formatting Reverse Mapping Code
3.3.3. Schema File DTD

Kodo JDO's reverse mapping tool generates persistent class definitions, complete with JDO metadata and Kodo mapping extensions, from an existing database schema. The reverse mapping tool has been tested with the following databases:

To use the reverse mapping tool, follow the steps below:

  1. Use the R&D schema generator to export your current schema to an XML schema file. The R&D schema generator can be run via the included rd-schemagen script or through its Java class, com.solarmetric.rd.kodo.impl.jdbc.schema.SchemaGenerator . Spcify the file to write to with the -file or -f command-line flag. For example:

    rd-schemagen -file schema.xml
    

    You can limit the schemas and/or tables the schema generator looks at with the -schemas or -s command-line argument. This argument expects a comma-separated list of schema and table names. Specify table names in the form <schema-name>.<table-name>. If a target table does not have a schema or you do not know its schema, list its name as .<table-name>. The example below will generate XML for the entire BUSOBJS schema, the ADDRESS table in the GENERAL schema, and the SYSTEM_INFO table, regardless of what schema it is in (or it may not have a schema).

    rd-schemagen -file schema.xml -schemas BUSOBJS,GENERAL.ADDRESS,.SYSTEM_INFO
    

  2. Examine the generated schema file. JDBC drivers often provide incomplete or faulty metadata, in which case the file will not exactly match the actual schema. Alter the XML file to match the true schema. The XML format of the schema file is given here.

    After fixing any errors in the schema file, modify the XML to include foreign keys between all related tables. The schema generator will have automatically detected existing foreign key constraints; many schemas, however, do not employ database foreign keys for every table relation. By manually adding any missing foreign keys, you will give the reverse mapping tool the information it needs to reflect the proper relations between the persistent classes it creates.

  3. Run the reverse mapping tool on the finished schema file. (If you do not supply the schema file to reverse map, the tool will run directly against the database). The tool can be run via the included rd-reversemappingtool script, or through its Java class, com.solarmetric.rd.kodo.impl.jdbc.meta.ReverseMappingTool . In addition to the standard configuration flags accepted by all Kodo JDO tools, the reverse mapping tool recognizes the following command line flags:

    • -schemas/-s <schema and table names> : A comma-separated list of schema and table names to reverse map, if no XML schema file is supplied. The format of the list is the same as that described for the R&D schema generator above.

    • -package/-pkg <package name>: The package name of the generated classes. If no package name is given, the generated code will not contain package declarations.

    • -directory/-d <output directory> : The path to the directory to output all generated code and metadata to. If the directory does not match the package of a class, the package structure will be created beneath this directory. Defaults to the current directory.

    • -useSchemaName/-sn <true/t | false/f> : Set this flag to true to include the schema as well as table name in the name of each generated class. This can be useful when dealing with multiple schemas with same-named tables.

    • -useForeignKeyName/-fkn <true/t | false/f>: Set this flag to true if you would like field names for relations to be based on the database foreign key name. By default, relation field names are derived from the name of the related class.

    • -nullableAsObject/-no <true/t | false/f> : By default, all non-foreign key columns are mapped to primitives. Set this flag to true to generate primitive wrapper fields instead for columns that allow null values.

    • -primaryKeyOnJoin/-pkj <true/t | false/f> : The standard reverse mapping tool behavior is to map all tables with primary keys to persistent classes. If your schema has primary keys on many-many join tables as well, set this flag to true to avoid creating classes for those tables.

    • -useDatastoreIdentity/-ds <true/t | false/f>: Set to true to use datastore JDO identity for tables that have single numeric primary key columns. The tool typically uses application identity for all generated classes.

    • -oneToManyRelations/-omr <true/t | false/f>: Set to false to prevent the creation of inverse one-many relations for all one-one relations.

    • -metadata/-md <package | class> : Whether to write a single package-level JDO metadata file, or to write a JDO metadata file per generated class. Defaults to package.

      [Note]Note

      If you are using Kodo's JBuilder integration features, make sure to specify the -metadata class flag to write a separate JDO metadata file per class.

    • -customizerClass/-cc <class name> : The full class name of a com.solarmetric.rd.kodo.impl.jdbc.meta.ReverseCustomizer customization plugin. If you do not specify a reverse customizer of your own, the system defaults to a PropertiesReverseCustomizer . This customizer allows you to specify simple customization properties in the properties file given with the -customizerProperties flag below. We present the available property keys in the below.

    • -customizerProperties/-cp <properties file or resource>: The path or resource name of a properties file to pass to the reverse customizer on initialization.

    • -customizer./-c.<property name> <property value>: The given property name will be matched with the corresponding Java bean property in the specified reverse customizer, and set to the given value.

    • -codeFormat./-cf.<property name> <property value>: Code formatting properties. We enumerate code formatting options below .

    Example 3.3. Using the Reverse Mapping Tool

    rd-reversemappingtool -package com.xyz -directory ~/src schema.xml
    

    Running the tool will generate .java files for each generated class, package-level or per-class .jdo files, depending on the -metadata flag, and <package-name>.mapping file. The mapping file contains the O/R mapping information for the generated classes. Mapping files like these will be offered as an optional alternative to O/R metadata extensions in a future version of Kodo JDO. For now, you must import the mapping information into Kodo JDO metadata extensions.

  4. To import the generated O/R mapping information into metadata extensions, run the R&D import tool on the mapping file. The tool can be invoked via the included rd-importtool script or through its Java class, com.solarmetric.rd.kodo.impl.jdbc.meta.compat.ImportTool. Make sure to compile the generated classes before the import:

    javac *.java
    rd-importtool jdo.mapping
    

    You can now delete the mapping file if desired.

  5. Examine the generated classes and metadata, and modify them as necessary.

3.3.1. Customizing Reverse Mapping

The reverse mapping process can be customized with the com.solarmetric.kodo.impl.jdbc.meta.ReverseCustomizer plugin interface. See the class Javadoc for details on the hooks this interface provides. Specify the concrete plugin implementation to use with the -customizerClass/-cc command-line flag, described in the preceding section.

By default, the reverse mapping tool uses a com.solarmetric.kodo.impl.jdbc.meta.PropertiesReverseCustomizer . This customizer allows you to perform relatively simple customizations through the properties file named with the -customizerProperties tool flag. The customizer recognizes the following properties:

  • <class name>.rename <new class name> : Override the given tool-generated class name with a new value. Use full class names, including packages. You are free to rename a class to a new package. Specify a value of none to reject the class and leave the corresponding table unmapped.

  • <class name>.identity <datastore | identity class name>: Set this property to datastore to use datastore identity for the named class, rather than the default application identity. Any value other than datastore will be considered a new class name for the generated application identity class. Give full class names, including packages. You are free to change the package of the identity class this way. If the persistent class has been renamed, use the new class name for this property key. Remember that datastore identity requires a table with a single numeric primary key column.

  • <class name>.<field name>.rename <new field name>: Override the tool-generated field name with the given one. Use the field owner's full class name in the property key. If the field owner's class was renamed, use the new class name. The property value should be the new field name, without the preceding class name. Use a value of none to reject the generated mapping and remove the field from the class.

  • <class name>.<field name>.type <field type>: The type to give the named field. Use full class names. If the field or the field's owner class has been renamed, use the new name.

  • <class name>.<field name>.value : The initial value for the named field. The given string will be placed as-is in the generated Java code, so be sure to add quotes to strings, etc. If the field or the field's owner class has been renamed, use the new name.

All property keys are optional; if not specified, the customizer keeps the default value generated by the reverse mapping tool.

Example 3.4. Customizing Reverse Mapping with Properties

reversemappingtool -pkg com.xyz -cp custom.properties schema.xml

Example custom.properties:

com.xyz.TblMagazine.rename:             com.xyz.Magazine
com.xyz.TblArticle.rename:              com.xyz.Article
com.xyz.TblPubCompany.rename:           com.xyz.pub.Company
com.xyz.TblSysInfo.rename:              none

com.xyz.Magazine.allArticles.rename:    articles
com.xyz.Magazine.articles.type:         java.util.Collection
com.xyz.Magazine.articles.value:        new TreeSet()
com.xyz.Magazine.identity:              datastore

com.xyz.pub.Company.identity:           com.xyz.pub.CompanyId

3.3.2. Formatting Reverse Mapping Code

The reverse mapping tool accepts a set of command-line flags for formatting its output to match your coding style. All code formatting flags can begin with either the codeFormat or cf prefix.

  • -codeFormat./-cf.tabSpaces <spaces> : The number of spaces that make up a tab, or 0 to use tab characters. Defaults to using tab characters.

  • -codeFormat./-cf.spaceBeforeParen <true/t | false/f>: Whether or not to place a space before opening parentheses on method calls, if statements, loops, etc. Defaults to false.

  • -codeFormat./-cf.spaceInParen <true/t | false/f>: Whether or not to place a space within parentheses; i.e. method( arg ). Defaults to false.

  • -codeFormat./-cf.braceOnSameLine <true/t | false/f>: Whether or not to place opening braces on the same line as the declaration that begins the code block, or on the next line. Defaults to true.

  • -codeFormat./-cf.braceAtSameTabLevel <true/t | false/f>: When the braceOnSameLine option is disabled, you can choose whether to place the brace at the same tab level of the contained code.

  • -codeFormat./-cf.scoreBeforeFieldName <true/t | false/f>: Whether to prefix an underscore to names of private member variables.

  • -codeFormat./-cf.linesBetweenSections <lines> : The number of lines to skip between sections of code. Defaults to 2.

Example 3.5. Code Formatting with the Reverse Mapping Tool

rd-reversemappingtool -package com.xyz -codeFormat.spaceBeforeParen true -schemas BUSOBJS,OBJS 

3.3.3. Schema File DTD

<!ELEMENT schemas (schema)+>
<!ELEMENT schema (table)*>
<!ATTLIST schema name CDATA #IMPLIED>
<!ELEMENT table (column|index|pk|fk)+>
<!ATTLIST table name CDATA #REQUIRED>
<!ELEMENT column EMPTY>
<!ATTLIST column name CDATA #REQUIRED> 
<!ATTLIST column type (array | bigint | binary |
    bit | blob | char | clob | date | decimal |
    distinct | double | float | integer | java_object |
    longvarbinary | longvarchar | null | numeric | other |
    real | ref | smallint | struct | time | timestamp |
    tinyint | varbinary | varchar) #REQUIRED>
<!ATTLIST column not-null (true|false) "false">
<!ATTLIST column default CDATA #IMPLIED>
<!ATTLIST column size CDATA #IMPLIED>
<!ATTLIST column decimal-digits CDATA #IMPLIED>

<!-- the 'column' attribute of indexes, pks, and fks can be used -->
<!-- when the element has only one column (or for foriegn keys,  -->
<!-- only one local column); in these cases the on/join child    -->
<!-- elements can be ommitted                                    -->
<!ELEMENT index (on)*>
<!ATTLIST index name CDATA #REQUIRED>
<!ATTLIST index column CDATA #IMPLIED>
<!ATTLIST index unique (true|false) "false">
<!ELEMENT pk (on)*>
<!ATTLIST pk name CDATA #IMPLIED>
<!ATTLIST pk column CDATA #IMPLIED>
<!ELEMENT on EMPTY>
<!ATTLIST on column CDATA #REQUIRED>
<!ELEMENT fk (join)*>
<!ATTLIST fk name CDATA #IMPLIED>
<!ATTLIST fk to-table CDATA #REQUIRED>
<!ATTLIST fk column CDATA #IMPLIED>
<!ATTLIST fk delete-action (cascade|default|exception|none|null) "none">
<!ELEMENT join EMPTY>
<!ATTLIST join column CDATA #REQUIRED>
<!ATTLIST join to-column CDATA #REQUIRED>