6.9. Storing Second Class Objects via Stringification
Prev | Chapter 6. Standard Features | Next

## 6.9. Storing Second Class Objects via Stringification

Object fields that are neither instances of `PersistenceCapable` nor one of the default persistent types mandated by the JDO specification (primitives and wrappers, `String`, `Locale`, etc.) are, by default, persisted to blob fields by serializing the object. This has a number of drawbacks:

- serialization can be slow in some cases

- serialized fields can be larger than are necessary in some cases

- serialized fields cannot be queried

- since the database will store the java serialized bytes, other non-java applications are not able to share the data with the JDO application in a meaningful way

Kodo offers the ability to write custom field mappings in order to have complete control over the mechanism with which fields are stored, queried, and loaded from the datastore. Furthermore, since simple types can often be represented as a string (such as `java.net.URL`), Kodo provides metadata extensions to allow the simple storage of classes via stringification/destringification without having to write any custom code.

The field-level externalizer metadata extension will contain the name of a method that will be invoked to convert the field into a `String` for storage in the database. This extension can take either the name of a non-static method, which will be invoked on the field value, or a static method, which will be invoked with the field value as a parameter. Static methods can be specified using the format "`ClassName`.`method`".

The field-level factory metadata extension will contain the name of a method that will be invoked to instantiate the field from the `String` stored in the database. This extension will take a static method name, which will be invoked with the String value of the datastore value, and must return an instance of the field type. If this extension is not specified, Kodo will use the constructor of the field type that takes a single String argument, or will throw a `JDOFatalException` if no constructor with that signature exists.

Example 6.3. Metadata for persisting field of type java.lang.Class

    <!-- A field of type Class, stored by the class name. It
         will be saved with the getName method, and
         re-instantiated with the static forName factory method. -->
    <field name="clas" default-fetch-group="true">
        <extension vendor-name="kodo" key="externalizer" value="getName" />
        <extension vendor-name="kodo" key="factory" value="forName" />
    </field>

Example 6.4. Metadata for persisting field of type java.net.URL

    <!-- The field of type jave.net.URL can be externalized to
         a String with the URL.toExternalForm method. Since we
         do not specify the "factory" extension, the String
         constructor for URL will be used to recreate it.
         Since the constructor does not deal with nulls, we
         specify that the JDO layer should throw an exception
         when we attempt to store a null value for the field. -->
    <field name="url" null-value="exception" default-fetch-group="true">
        <extension vendor-name="kodo" key="externalizer" value="toExternalForm" />
    </field>

Example 6.5. Metadata for persisting field of type java.net.InetAddress

    <!-- The field of type java.net.InetAddress can
         be externalized with the InetAddress.getHostAddress()
         method, and restored with InetAddress.getByName(String).
         Note that we could also store the host name by having
         the externalizer instead by InetAddress.getHostName(), but
         that would result in very slow DNS queries every time the
         object is resored. Nulls are not handled by the
         factory, so we set them to throw an exception at
         the JDO level. Since the factory method might be slow,
         we set the default-fetch-group to be false, so the
         address will not be instantiated until it is needed. -->
    <field name="address" null-value="exception"
        default-fetch-group="false">
        <extension vendor-name="kodo" key="externalizer" value="getHostAddress" />
        <extension vendor-name="kodo" key="factory" value="InetAddress.getByName" />
    </field>

Example 6.6. Metadata for persisting field of type java.util.TimeZone

    <!-- The field of type java.util.TimeZone can be
         constructed with the static TimeZone.getTimeZone
         factory, and externalized via TimeZone.getID. Note that
         that TimeZone is an abstract class; since we have
         a static factory method, this isn't a problem. -->
    <field name="timeZone" null-value="exception"
        default-fetch-group="true">
        <extension vendor-name="kodo" key="externalizer" value="getID" />
        <extension vendor-name="kodo" key="factory" value="TimeZone.getTimeZone" />
    </field>

Example 6.7. Metadata for persisting field of type java.io.File

    <!-- java.io.File objects are represented by the
         externalizer File.getAbsolutePath. File's String
         constructor will be used to restore the File from
         the String. -->
    <field name="file" null-value="exception"
        default-fetch-group="true">
        <extension vendor-name="kodo" key="externalizer" value="getAbsolutePath" />
    </field>

Note that by default, stringification fields are not in the default fetch group, so they will be loaded in a secondary statement when first accessed. This can be overridden by setting the `default-fetch-group` attribute of the field to be `true`.

| Note
As with fields that are stored through serialization, it is not possible to perform queries against fields that are stored via stringification. Furthermore, since access to the field is not mediated, changes to the internal state of the field will not be detected by the JDO's StateManager, and thus the field will only be marked dirty if the field is explicitely dirtied or reset.

Prev | Up | Next
6.8. Custom Class Indicators | Home | 6.10. Primary Key Generation