5.4. Mutable Second Class Object Fields

5.4.1. Restoring Mutable Fields
5.4.2. Typing and Ordering
5.4.3. Proxies
5.4.3.1. Smart Proxies
5.4.3.2. Large Result Set Proxies
5.4.3.3. Custom Proxies

Mutable second class objects consist of collections, maps, and other persistent field values that can be modified directly. This section documents some aspects of Kodo's handling of these fields that may affect the way you design your persistent classes.

5.4.1. Restoring Mutable Fields

JDO requires that all immutable fields be restored to their pre-transaction state when a transaction rollback occurs. Kodo also has the ability to restore the state of mutable fields including collections, maps, and arrays. To have the state of mutable fields restored on rollback, set the kodo.RestoreMutableValues configuration property to true.

5.4.2. Typing and Ordering

Kodo attempts to preserve the Java type and order of all collections and maps. By default, Kodo uses an ordering column to maintain sequencing for all fields declared to be a List type. You can turn sequencing on or off for these and other fields through the jdbc-ordered metadata extension. When loading data into a field, Kodo also examines the value you assign the field in your declaration code or in your no-args constructor. If the field value's type is more specific than the field's declared type, Kodo uses the value type to hold the loaded data. Kodo also uses the comparator you've initialized the field with, if any. Therefore, you can use custom comparators on your persistent field simply by setting up the comparator and using it in your field's initial value.

Example 5.8. Using Initial Field Values

public class Company
{
    // Kodo will detect the custom comparator in the initial field value
    // and use it whenever loading data from the database into this field
    private Collection employeesBySal = new TreeSet (new SalaryComparator ());
    private Map departments;

    public Company
    {
        // or we can initialize fields in our no-args constructor; even though
        // this field is declared type Map, Kodo will detect that it's actually
        // a TreeMap and use natural ordering for loaded data
        departments = new TreeMap ();
    }

    // rest of class definition...
}

5.4.3. Proxies

At runtime, the values of all mutable second class object fields in persistent and transactional objects are replaced with implementation-specific proxies. On modification, these proxies notify their owning instance that they have been changed, so that the appropriate updates can be made on the data store. Kodo extends this standard JDO proxying behavior with smart proxies and custom proxies.

5.4.3.1. Smart Proxies

Most proxies only track whether or not they have been modified. Smart proxies for collection and map fields, however, keep a record of which elements have been added, removed, and changed. This record enables the Kodo JDO runtime to make more efficient database updates on these fields.

When designing your persistent classes, keep in mind that you can optimize for Kodo JDO by using fields of type java.util.Set, java.util.TreeSet, and java.util.HashSet for your collections whenever possible. Smart proxies for these types are more efficient than proxies for Lists. You can also design your own smart proxies to further optimize Kodo JDO for your usage patterns. See the section on custom proxies for details.

5.4.3.2. Large Result Set Proxies

Under standard JDO behavior, traversing a persistent collection or map field brings the entire contents of that field into memory. Some persistent fields, however, might represent huge amounts of data, to the point that attempting to fully instantiate them can overwhelm the JVM or seriously degrade performance.

Kodo uses special proxy types to represent these "large result set" fields. Kodo's large result set proxies do not cache any data in memory. Instead, each operation on the proxy offloads the work to the database and returns the proper result. For example, the contains method of a large result set collection will perform a SELECT COUNT(*) query with the proper where conditions to find out if the given element exists in the database's record of the collection. Similarly, each time you obtain an iterator Kodo performs the proper query using the current persistence manager's large result set settings, as discussed in the JDBC chapter. As you invoke Iterator.next, Kodo instantiates the result objects on-demand. You can free the resources used by a large result set iterator by passing it to the static KodoHelper.close method.

Example 5.9. Using a Large Result Set Iterator

import kodo.runtime.*;

...

Collection employees = company.getEmployees (); // employees is a lrs collection
Iterator itr = employees.iterator ();
try
{
    while (itr.hasNext ())
        process ((Employee) itr.next ());
}
finally
{
    KodoHelper.close (itr);
}

You can also add and remove from large result set proxies, just as with standard fields. Kodo keeps a record of all changes to the elements of the proxy, which it uses to make sure the proper results are always returned from collection and map methods, and to update the field's database record on commit.

In order to use large result set proxies, you must mark each large result set field with the lrs metadata extension. The extension takes a value of true or false. The following restrictions apply to large result set fields:

  • The field must be declared as either a java.util.Collection or java.util.Map. It cannot be declared as any other type, including any sub-interface of collection or map, or any concrete collection or map class.

  • The field cannot have an externalizer (see Section 7.9.23, “Externalization”).

  • Because they rely on their owning object for context, large result set proxies cannot be transferred from one persistent field to another. The following code would result in an error on commit:

    Collection employees = company.getEmployees ()  // employees is a lrs collection
    company.setEmployees (null);
    anotherCompany.setEmployees (employees);
    

Example 5.10. Marking a Large Result Set Field

<class name="Company">
    <field name="employees">
        <collection element-type="Employee"/>
        <extension vendor-name="kodo" key="lrs" value="true"/>
    </field>
    ...
</class>

5.4.3.3. Custom Proxies

In Kodo JDO, proxies are managed through the kodo.util.ProxyManager interface. Kodo JDO includes a default proxy manager, the kodo.util.ProxyManagerImpl (with a plugin alias name of default), that will meet the needs of most users. The default proxy manager understands the following configuration properties:

  • TrackChanges: Whether to use smart proxies. Defaults to true.

  • AssertAllowedType: Whether to check elements of maps and collections as they are added to make sure they are of the type defined as the key-type, value-type, or element-type defined in the JDO metadata. Defaults to false .

For custom behavior, Kodo JDO allows you to define your own proxy classes, and your own proxy manager. See the kodo.util package Javadoc for details on the interfaces involved, and the utility classes Kodo JDO provides to assist you.

You can plug your custom proxy manager into the Kodo JDO runtime through the kodo.ProxyManager configuration property.

Your Kodo JDO distribution includes custom proxy samples in the samples/proxies directory.

Example 5.11. Configuring the Proxy Manager

kodo.ProxyManager: AssertAllowedType=true, TrackChanges=false