4.5. JDO Identity
Prev | Chapter 4. PersistenceCapable | Next

## 4.5. JDO Identity

Java recognizes two forms of object identity: numeric identity and qualitative identity. If two references are numerically identical, then they refer to the same JVM instance in memory. You can test for this using the `==` operator. Qualitative identity, on the other hand, relies on some user-defined criteria to determine whether two objects are "equal". You test for qualitative identity using the `equals` method. By default, this method simply relies on numeric identity.

JDO introduces another form of object identity, called JDO identity. JDO identity tests whether two persistent objects represent the same state in the data store.

The JDO identity of each persistent instance is encapsulated in its JDO identity object. You can obtain the JDO identity object for a persistent instance through the `JDOHelper`'s `getObjectId` method. If two JDO identity objects compare equal using the `equals` method, then the two corresponding persistent objects represent the same state in the data store.

Example 4.4. JDO Identity Objects

    /**
     * This method tests whether the given persistent objects represent the
     * same data store record.  It returns false if either argument is not
     * a persistent object.
     */
    public boolean persistentEquals (Object obj1, Object obj2)
    {
        Object jdoId1 = JDOHelper.getObjectId (obj1);
        Object jdoId2 = JDOHelper.getObjectId (obj2);
        return jdoId1 != null && jdoId1.equals (jdoId2);
    }

If you are dealing with a single `PersistenceManager`, then there is an even easier way to test whether two persistent object references represent the same state in the data store: the `==` operator. JDO requires that each `PersistenceManager` maintain only one JVM object to represent each unique data store record. Thus, JDO identity is equivalent to numeric identity within a `PersistenceManager`'s cache of managed objects. This is referred to as the uniqueness requirement.

The uniqueness requirement is extremely important -- without it, it would be impossible to maintain data integrity. Think of what could happen if two different objects of the same `PersistenceManager` were allowed to represent the same persistent data. If you made different modifications to each of these objects, which set of changes should be written to the data store? How would your application logic handle seeing two different "versions" of the same data? Thanks to the uniqueness requirement, these questions do not have to be answered.

There are three types of JDO identity, but only two of them are important to most applications: datastore identity and application identity. The majority of JDO implementations support datastore identity at a minimum; many support application identity as well. All persistent classes in an inheritance tree must use the same form of JDO identity.

| Note
Kodo JDO supports both datastore and application identity.

### 4.5.1. Datastore Identity

Datastore identity is managed by the JDO implementation. It is independent of the values of your persistent fields. You have no say over what class is used for JDO identity objects, or what data is used to create identity values. The only requirement placed on JDO vendors implementing datastore identity is that the class they use for JDO identity objects meets the following criteria:

- The class must be public.

- The class must be serializable.

- All non-static fields of the class must be public and serializable.

- The class must have a public no-args constructor.

- The class must have a public `String` constructor. It must override the `toString` method to return a string that can be used by this constructor to create a new JDO identity object that compares equal to the instance the string was obtained from.

The last criterion listed is particularly important. As you will see in the chapter on `PersistenceManager`s, it allows you to store the identity object for a persistent instance as a string, then later recreate the identity object and retrieve the corresponding persistent instance.

| Note
Kodo JDO allows you to customize the manner in which datastore identity values are generated.

### 4.5.2. Application Identity

Application identity is managed by you, the developer. Under application identity, the values of one or more persistent fields in an object determine its JDO identity. The fields whose values make up the object's identity are called primary key fields. Each object's primary key field values must be unique among all other objects of the same type.

When using application identity, the `equals` and `hashCode` methods of the persistence-capable class must depend on all of the primary key fields.

| Note
Kodo does not depend upon this behavior. However, some JDO implementations do, so it should be implemented if portability is a concern.

Also, when using application identity, it is up to you to supply the class used for JDO identity objects. This application identity class must meet all of the criteria listed for datastore identity classes. It must also obey the following requirements:

- The names of the non-static fields of the class must include the names of the primary key fields of the corresponding persistence-capable class, and the field types must be identical.

- The `equals` and `hashCode` methods of the class must use the values of all fields corresponding to primary key fields in the persistence-capable class.

- If the class is an inner class, it must be `static`.

- All classes related by inheritance must use the same application identity class.

- Each inheritance tree must use a unique application identity class.

- Primary key fields must be primitives, primitive wrappers, or Strings. Notably, other persistent instances can not be used as primary key fields.

These criteria allow you to construct an application identity object from either the values of the primary key fields of a persistent instance, or from a string produced by the `toString` method of another identity object.

Though it is not a requirement, you should also use your application identity class to register the corresponding persistence-capable class with the JVM. This is typically accomplished with a static block in the application identity class code, as the example below illustrates. This registration process is a workaround for a quirk in JDO's persistent type registration system whereby some by-id lookups might fail if the type being looked up hasn't been used yet in your application.

| Note
Though you may still create application identity classes by hand, Kodo JDO provides the `appidtool` to automatically generate proper application identity classes based on your primary key fields.

Example 4.5. Application Identity Class

    /**
     * Persistent class using application identity.
     */
    public class Magazine
    {
        private String isbn;    // primary key field
        private String title;   // primary key field

        /**
         * Equality must be implemented in terms of primary key field
         * equality, and must use instanceof rather than comparing
         * classes directly.
         */
        public boolean equals (Object other)
        {
            if (other == this)
                return true;
            if (!(other instanceof Magazine))
                return false;

            Magazine mag = (Magazine) other;
            return ((isbn == null && mag.isbn == null)
                || (isbn != null && isbn.equals (mag.isbn)))
                && ((title == null && mag.title == null)
                || (title != null && title.equals (mag.title)));
        }

        /**
         * Hashcode must also depend on primary key values.
         */
        public int hashCode ()
        {
            return ((isbn == null) ? 0 : isbn.hashCode ())
                + ((title == null) ? 0 : title.hashCode ())
                % Integer.MAX_VALUE;
        }

        // rest of fields and methods omitted

        /**
         * Application identity class for Magazine.
         */
        public static class MagazineId
        {
            static
            {
                // register Magazine with the JVM
                Class c = Magazine.class;
            }

            // each primary key field in the Magazine class must have a
            // corresponding public field in the identity class
            public String isbn;
            public String title;

            /**
             * Default constructor requirement.
             */
            public MagazineId ()
            {
            }

            /**
             * String constructor requirement.
             */
            public MagazineId (String str)
            {
                int idx = str.indexOf (':');
                isbn = str.substring (0, idx);
                title = str.substring (idx + 1);
            }

            /**
             * toString must return a string parsable by the string constructor.
             */
            public String toString ()
            {
                return isbn + ":" + title;
            }

            /**
             * Equality must be implemented in terms of primary key field
             * equality, and must use instanceof rather than comparing
             * classes directly (some JDO implementations may subclass JDO
             * identity class).
             */
            public boolean equals (Object other)
            {
                if (other == this)
                    return true;
                if (!(other instanceof MagazineId))
                    return false;

                MagazineId mi = (MagazineId) other;
                return ((isbn == null && mi.isbn == null)
                    || (isbn != null && isbn.equals (mi.isbn)))
                    && ((title == null && mi.title == null)
                    || (title != null && title.equals (mi.title)));
            }

            /**
             * Hashcode must also depend on primary key values.
             */
            public int hashCode ()
            {
                return ((isbn == null) ? 0 : isbn.hashCode ())
                    + ((title == null) ? 0 : title.hashCode ())
                    % Integer.MAX_VALUE;
            }
        }
    }

Prev | Up | Next
4.4. InstanceCallbacks | Home | 4.6. Conclusions