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. |