Chapter 8. XML code generation - Extensions

8.1. XML code generation extensions - Motivation

With Castor 1.2 and earlier releases it has already been possible to generate Java classes from an XML schema and use these classes for XML data binding without having to write a mapping file.

This is possible because the Castor XML code generator generated - in addition to the domain classes - a set of XML descriptor classes as well, with one descriptor class generated per generated domain class. It's this XML descriptor class that holds all the information required to map Java classes and/or field members to XML artifacts, as set out in the original XML schema definitions. This includes ....

  • artefact names

  • XML namespace URIs

  • XML namespace prefix

  • validation code

Starting with Castor 1.3, a mechanism has been added to the XML code generator that allows extension of these core offerings so that either additional content is added to the generated domain classes additonal descriptor classes are gernated.

8.1.1. JDO extensions for the Castor XML code generator

8.1.1.1. JDO extensions - Motivation

With Castor 1.2 and previous releases it was already possible to generate Java classes from an XML schema and use these classes for XML data binding without having to write a mapping file.

This is possible because the Castor XML code generator generated - in addition to the domain classes - a set of XML descriptor classes as well, with one descriptor class generated per generated domain class. It's this XML descriptor class that holds all the information required to map Java classes and/or field members to XML artifacts, as set out in the original XML schema definitions. This includes ....

  • artefact names

  • XML namespace URIs

  • XML namespace prefix

  • validation code

In addition, it was already possible to use the generated set of domain classes in Castor JDO for object-/relational mapping purpose by supplying a (manually written) JDO-specific mapping file. Whilst technically not very difficult, this was still an error-prone task, especially in a context where tens or hundreds of classes were generated from a set of XML schemas.

The JDO extensions for the Castor XML code generator extend the code generator in such a way that a second set of descriptor classes is generated: the JDO descriptor classes. These new descriptor classes define the mapping between Java (domain) objects and database tables/columns, and as such remove the requirement of having to write a JDO-specific mapping file.

[Note]Note

Please note that Castor JDO - upon startup - internally converts the information provided in the JDO mapping file to (JDO) descriptor classes. As such, the approach outlined above simply re-uses an existing code base and just automates the production of those descriptor classes.

The following sections introduce the general principles, define the XML schema artifacts available to annotate an existing XML schema and highlight the usage of these artifacts by providing examples. At the same time, a limited set of current product limitations are spelled out.

8.1.1.2. Limitations

With release 1.3 of Castor, the following limitations exist for the JDO extensions of the XML code generator:

  1. The extensions currently can only be used in type mode of the XML code generator.

  2. There's currently no support for key generators. There's work in progress to add this functionality, though.

  3. There's currently no support for bidirectional relations, modelled through the use of <xs:id> and <xs:idref> constructs.

8.1.1.3. Prerequisites

To facilitate the detailed explanations in the following sections, we now define a few <complexType> definitions that we want to map against an existing database schema, and the corresponding SQL statements to create the required tables.

8.1.1.3.1. Sample XML schemas
               
<complexType name="bookType">
   <sequence>
      <element name="isbn" type="xs:string" />
      <element name="pages" type="xs:integer" />
      <element name="lector" type="lectorType" />
      <element name="authors" type="authorType" maxOccurs="unbounded" />
   </sequence>
</complexType>

<complexType name="lectorType">
   <sequence>
      <element name="siNumber" type="xs:integer" />
      <element name="name" type="xs:string" />
   </sequence>
</complexType>

<complexType name="authorType">
   <sequence>
      <element name="siNumber" type="xs:integer" />
      <element name="name" type="xs:string" />
   </sequence>
</complexType>
            
8.1.1.3.2. Sample DDL statements
CREATE TABLE author_table (
   sin INTEGER NOT NULL,
   name VARCHAR(20) NOT NULL
);

CREATE TABLE lector_table (
   sin INTEGER NOT NULL,
   name VARCHAR(20) NOT NULL
);

CREATE TABLE book_table (
   isbn VARCHAR(13) NOT NULL,
   pages INTEGER,
   lector_id INTEGER NOT NULL,
   author_id INTEGER NOT NULL
);

8.1.1.4. Configuring the XML code generator

To have the Castor XML code generator generate JDO class descriptors when processing a set of XML schemas, please use one of the following methods:

Table 8.1. Accessing options

UsageMethodDescription
SourceGeneratorsetJdoDescriptorCreation(boolean)Supply a value of true to enable this feature.
SourceGeneratorMainFlag -gen-jdo-descSet this optional flag to enable this feature.
Ant task for XML code generatorgenerateJdoDescriptors optionSet this to a value of true.

8.1.1.5. The JDO annotations for XML schemas

This section enlists the XML artifacts available to annotate an existing XML schema with JDO extension-specific information. These constructs are defined themselves in an XML schema jdo-extensions.xsd that has a target namespace of http://www.castor.org/binding/persistence.

To enable proper validation of your XML schemas when editing JDO annotations, and to enable XML completion in your preferred XML editor, please add schemaLocation information to your XML schema definition as follows:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    targetNamespace="http://your/target/namespace"
    xmlns:jdo="http://www.castor.org/binding/persistence"
    xmlns="http://your/target/namespace"                                       (1)
    xsi:schemaLocation="http://www.castor.org/binding/persistence http://www.castor.org/jdo-extensions.xsd">
    
...

</xs:schema>        
            

where ...

1

The values supplied in the schemaLocation attribute define the location of the XML schema for any XML artefacts bound to the http://www.castor.org/binding/persistence namespace.

8.1.1.5.1. <table> element

The <table> element allows you to map an <complexType> definition to a database table within a database, and to specify the identity (frequently referred to as primary key), as follows:

<xs:complexType name="authorType">
   <xs:annotation>
      <xs:appinfo>
         <jdo:table name="author_table">                                       (1)
            <jdo:primary-key>                                                  (2)
               <jdo:key>siNumber</jdo:key>
            </jdo:primary-key>
         </jdo:table>
      </xs:appinfo>
   </xs:annotation>
   <xs:sequence>
      <xs:element name="siNumber" type="xs:integer" />
      <xs:element name="name" type="xs:string" />
   </xs:sequence>
</xs:complexType>
              

where ...

1

The <jdo:table ...> defines the name of the database table to which the complex type definition authorType should be mapped.

2

The <jdo:primary-key> indicates which artifacts of the content model of the complex type definition should be used as the corresponding object identity; in database terms, this is often referred to as primary key.

Above example maps the complex type authorType to the table author_table, and specifies that the member siNumber be used as object identity.

The XML schema definition for the <table> element is defined as follows:

<xs:element name="table">
   <xs:complexType>
      <xs:sequence>
         <xs:element name="primaryKey" type="jdo:pkType"/>
      </xs:sequence>
      <xs:attribute name="name" type="xs:string" use="required"/>
      <xs:attribute name="accessMode" use="optional" default="shared">
         <xs:simpleType>
            <xs:restriction base="xs:string">
               <xs:enumeration value="read-only"/>
               <xs:enumeration value="shared"/>
               <xs:enumeration value="exclusive"/>
               <xs:enumeration value="db-locked"/>
            </xs:restriction>
         </xs:simpleType>
      </xs:attribute>
      <xs:attribute name="detachable" type="xs:boolean" default="false"/>
   </xs:complexType>
</xs:element>

<xs:complexType name="pkType">
   <xs:sequence>
      <xs:element name="key" type="xs:string" maxOccurs="unbounded" />
   </xs:sequence>
</xs:complexType>
            
8.1.1.5.2. <column> element

The <column> element allows you to map a member of content model of a <complexType> definition to a column within a database table.

<xs:complexType name="authorType">
   <xs:annotation>
      <xs:appinfo>
         <jdo:table name="author_table">
            <jdo:primary-key>
               <jdo:key>siNumber</jdo:key>
            </jdo:primary-key>
         </jdo:table>
      </xs:appinfo>
   </xs:annotation>
   <xs:sequence>
      <xs:element name="siNumber" type="xs:integer" >
         <xs:annotation>
            <xs:appinfo>
                <jdo:column name="sin" type="integer" />                       (1)
            </xs:appinfo>
         </xs:annotation>
      </xs:element>
      <xs:element name="name" type="xs:string" />
   </xs:sequence>
</xs:complexType>
              

where ....

1

Defines that the element definition siNumber be mapped against the database column sin, and that the (database) type of this column is integer.

Above example maps the element isNumber to the database column sin, and specifies the database type to be used for persistence (integer, in this case).

The XML schema definition for <column> is defined as follows:

<xs:element name="column">
   <xs:complexType>
      <xs:complexContent>
         <xs:extension base="jdo:readonlyDirtyType">
            <xs:attribute name="name" type="xs:string" use="required" />
            <xs:attribute name="type" type="xs:string" use="required" />
            <xs:attribute name="acceptNull" type="xs:boolean" use="optional"
               default="true" />
         </xs:extension>
      </xs:complexContent>
   </xs:complexType>
</xs:element>
            

where the content is described as follows:

Table 8.2. <column> - Definitions

NameDescription
nameName of the column
typeJDO-type of the column
acceptNullWhether this field accepts NULL values or not

8.1.1.5.3. <one-to-one> element

The <one-to-one> element allows you to map a member of content model of a <complexType> definition to a 1:1 relation to another <complexType>.

<xs:complexType name="bookType">
   <xs:annotation>
      <xs:appinfo>
         <jdo:table name="book_type_table">
            <jdo:primary-key>
               <jdo:key>isbn</jdo:key>
            </jdo:primary-key>
         </jdo:table>
      </xs:appinfo>
   </xs:annotation>
   <xs:sequence>
      <xs:element name="isbn" type="xs:string" >
         <xs:annotation>
            <xs:appinfo>
                <jdo:column name="isbn" type="varchar" />
            </xs:appinfo>
         </xs:annotation>
      </xs:element>
      <xs:element name="pages" type="xs:integer" >
         <xs:annotation>
            <xs:appinfo>
                <jdo:column name="pages" type="integer" />
            </xs:appinfo>
         </xs:annotation>                                                      (1)
      </xs:element>
      <xs:element name="lector" type="lectorType" >
         <xs:annotation>
            <xs:appinfo>
               <jdo:one-to-one name="lector_id" />
            </xs:appinfo>
         </xs:annotation>
      </xs:element>
      <xs:element name="authors" type="authorType" maxOccurs="unbounded" >
         ...
      </xs:element>
   </xs:sequence>
</xs:complexType>
                

where ....

1

Defines a 1:1 relation to another <complexType>, additionally providing the necessary foreign key column at the database level.

Above example maps the element lector to a 1:1 relation to the complex type lectorType, and specifies the (column name of the) foreign key to be used (lector_id in this case).

The XML schema definition for <one-to-one> is defined as follows:

<xs:element name="one-to-one">
   <xs:complexType>
      <xs:complexContent>
         <xs:extension base="jdo:readonlyDirtyType">
            <xs:attribute name="name" type="xs:string"/>
         </xs:extension>
      </xs:complexContent>
   </xs:complexType>
</xs:element>
            

where the content is described as follows:

Table 8.3. <one-to-one> - Definitions

NameDescription
nameName of the column that represents the foreign key of this relation

8.1.1.5.4. <one-to-many> element

The <one-to-many> element allows you to map a member of the content model of a <complexType> definition as part of a 1:M relation to another <complexType>.

<xs:complexType name="bookType">
   <xs:annotation>
      <xs:appinfo>
         <jdo:table name="book_type_table">
            <jdo:primary-key>
               <jdo:key>isbn</jdo:key>
            </jdo:primary-key>
         </jdo:table>
      </xs:appinfo>
   </xs:annotation>
   <xs:sequence>
      <xs:element name="isbn" type="xs:string" >
         <xs:annotation>
            <xs:appinfo>
                <jdo:column name="isbn" type="varchar" />
            </xs:appinfo>
         </xs:annotation>
      </xs:element>
      <xs:element name="pages" type="xs:integer" >
         <xs:annotation>
            <xs:appinfo>
                <jdo:column name="pages" type="integer" />
            </xs:appinfo>
         </xs:annotation>
      </xs:element>
      <xs:element name="lector" type="lectorType" >
         <xs:annotation>
            <xs:appinfo>
               <jdo:one-to-one name="lector_id" />
            </xs:appinfo>
         </xs:annotation>
      </xs:element>
      <xs:element name="authors" type="authorType" maxOccurs="unbounded" >
         <xs:annotation>
            <xs:appinfo>
                <jdo:one-to-many name="book_id" />                             (1)
            </xs:appinfo>
         </xs:annotation>    
      </xs:element>
   </xs:sequence>
</xs:complexType>
                

where ....

1

Defines a 1:M relation to another <complexType>, additionally providing the necessary foreign key column for the many member at the database level.

Above example maps the element authors as part of a 1:M relation to the complex type authorType, and specifies the (column name of the) foreign key of the many member to be used (book_id in this case).

The XML schema definition for <one-to-many> is given as follows:

<xs:element name="one-to-many">
   <xs:complexType>
      <xs:complexContent>
         <xs:extension base="jdo:readonlyDirtyType">
            <xs:attribute name="name" type="xs:string" />
         </xs:extension>
      </xs:complexContent>
   </xs:complexType>
</xs:element>
            

with the following details applying:

Table 8.4. <one-to-many> - Definitions

NameDescription
nameName of the column that represents the (many) foreign key of this relation

8.1.1.6. Using the generated (domain) classes with Castor JDO

Once you have generated domain classes and descriptor classes (both XML and JDO) from your set of XML schemas, you'll be able to use them as are. There's a few minor changes, which we are going to highlight below, but the main benefit is that you not have to write a JDO mapping file.

8.1.1.6.1. Empty mapping file

As you have already generated JDO descriptor classes for each of your domain objects, you won't have to supply mappings for those classes anymore. As such, your mapping file will stay empty, as shown:

<?xml version="1.0"?>
<!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN"
                           "http://castor.org/mapping.dtd">
<mapping>

   <!-- no mappings required -->
</mapping>
            
[Note]Note

Please note that you can of course supply mappings for those classes that stand outside of the generation process from your XML schemas. It is possible, too, to match both modes. In other words, a domain class mapped manually will be able to refer to a domain class as generated.

8.1.1.6.2. Use of a JDOClassDescriptorResolver

In order for Castor to be able to access the generated (JDO) class descriptors and to load those classes from the file system, you will have to configure an instance of JDOClassDescriptorResolver and pass it to your JDOManager instance when loading the JDO configuration.

The following example shows how to configure Castor JDO so that the classes generated from the sample XML schema above can be used with CASTOR JDO seamlessly.

JDOClassDescriptorResolver resolver = new JDOClassDescriptorResolverImpl();
resolver.addClass(org.castor.jdo.extension.sample.BookType.class);
resolver.addClass(org.castor.jdo.extension.sample.LectorType.class);            
resolver.addClass(org.castor.jdo.extension.sample.AuthorType.class);

InputSource jdoConfiguration = ....;
JDOManager.loadConfiguration(jdoConfiguration, null, null, resolver);
   
JDOManager jdoManager = JDOManager.createInstance("jdo-extensions");
...             
            

Alternatively, if the classes generated from the sample XML schema shown above reside in the same package, you can configure the JDOClassDescriptorResolver as follows:

JDOClassDescriptorResolver resolver = new JDOClassDescriptorResolverImpl();
resolver.addPackage("org.castor.jdo.extension.sample");
...
            
[Tip]Tip

For the latter approach to work, you will have to make sure that the .castor.jdo.cdr files generated alongside your domain (and descriptor classes) are included in your application deployment units. If not, Castor JDO will not be able to load the descriptor classes from the file system, and throw an exception.

8.1.2. SOLRJ extensions for the Castor XML code generator

8.1.2.1. SOLRJ extensions - Motivation

With Castor 1.2 and previous releases it was already possible to generate Java classes from an XML schema and use these classes for XML data binding without having to write a mapping file.

This is possible because the Castor XML code generator generated - in addition to the domain classes - a set of XML descriptor classes as well, with one descriptor class generated per generated domain class. It's this XML descriptor class that holds all the information required to map Java classes and/or field members to XML artifacts, as set out in the original XML schema definitions. This includes ....

  • artefact names

  • XML namespace URIs

  • XML namespace prefix

  • validation code

The SOLRJ extensions for the Castor XML code generator extend the code generator in such a way that the set of domain classes is augmented with SOLRJ-specific @Field annotations.

The following sections introduce the general principles, define the XML schema artifacts available to annotate an existing XML schema and highlight the usage of these artifacts by providing examples.

8.1.2.2. Prerequisites

To facilitate the detailed explanations in the following sections, we now define a few <complexType> definitions that we want to be able to store in a SOLR index in addition to vanilla XML data binding funtionality.

8.1.2.2.1. Sample XML schemas
       
<complexType name="bookType">
   <sequence>
      <element name="isbn" type="xs:string" />
      <element name="pages" type="xs:integer" />
   </sequence>
</complexType>
        

8.1.2.3. The SOLRJ annotations for XML schemas

This section enlists the XML artifacts available to annotate an existing XML schema with SOLRJ extension-specific information. These constructs are defined themselves in an XML schema solrj-extensions.xsd that has a target namespace of http://www.castor.org/binding/solrj.

To enable proper validation of your XML schemas when editing SOLRJ annotations, and to enable XML completion in your preferred XML editor, please add schemaLocation information to your XML schema definition as follows:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    targetNamespace="http://your/target/namespace"
    xmlns:solr="http://www.castor.org/binding/solrj"
    xmlns="http://your/target/namespace"
    xsi:schemaLocation="http://www.castor.org/binding/solrj http://www.castor.org/solrj-extens(1)ions.xsd">
    
...

</xs:schema>        
            

where ...

1

The values supplied in the schemaLocation attribute define the location of the XML schema for any XML artefacts bound to the http://www.castor.org/binding/solrj namespace.

8.1.2.3.1. <field> element

The <field> element allows you to map a member of the content model of a <complexType> definition to SOLRJ field.

<complexType name="bookType">
   <sequence>
      <element name="isbn" type="xs:string">
         <xs:annotation>
            <xs:appinfo>
                <solrj:field name="id" />                                      (1)
            </xs:appinfo>
         </xs:annotation>
      </element>
      <element name="pages" type="xs:integer">
         <xs:annotation>
            <xs:appinfo>
                <solrj:field />                                                (2)
            </xs:appinfo>
         </xs:annotation>
      </element>
   </sequence>
</complexType>
              

where ....

1

Defines that the element definition isbn be mapped against the SOLRJ field id.

2

Defines that the element definition name be mapped to the SOLRJ field name.

Above example maps the element isbn to the SOLR index field id, and the element name to the identically-named SOLR index field. Please note that a SOLR index field name does not have to be specified if the field name and the Java property name are identical.

Above complex type definition will be transformed to the corresponding Java property definitions (within a class):

public class BookType {

    @Field("id")
    private String isbn;
    
    @Field
    private long pages;
    
}
            

The XML schema definition for <field> is defined as follows:

<xs:element name="field">
  <xs:annotation>
     <xs:documentation>
            Element 'field' is used to specify the use of the SOLRJ
            @Field annotation.
        </xs:documentation>
  </xs:annotation>
  <xs:complexType>
     <xs:attribute name="name" type="xs:string" use="optional">
        <xs:annotation>
           <xs:documentation>
                    Attribute 'name' is used to specify the name of
                    the index field to be mapped against; if not used,
                    the name of the Java property will be used as filed 
                    name.
                </xs:documentation>
        </xs:annotation>
     </xs:attribute>
  </xs:complexType>
</xs:element>
            

where the content is described as follows:

Table 8.5. <field> - Definitions

NameDescriptionOptional?
nameName of the SOLR index field.Yes

8.1.2.4. Using the generated domain classes with SOLR

Once you have generated domain classes (and descriptor classes for the XML binding) from your set of XML schemas, you'll be able to use them as are.