Guide  Reference  Contents  Previous  Next  Index



Persistent Objects

A persistent object is an object that has been assigned a storage location in a federated database. When you commit the transaction in which you create a persistent object, that object's data is saved in the database; the object can then be accessed by other processes. A persistent object continues to exist beyond the duration of the process that creates it. In contrast, a transient object exists only within the memory of the process that creates it; when that process terminates, the transient object ceases to exist.

In This Chapter

Making an Object Persistent
Immediate and Delayed Persistence
Assignment of Storage Location
Storing an Object Persistently
Working With a Persistent Object
Retrieving an Object From the Database
Locking a Persistent Object
Fetching an Object's Data
Modifying a Persistent Object
Copying a Persistent Object
Moving a Persistent Object
Deleting a Persistent Object
Avoiding Stale Cache Information
Internal Persistent Objects
Moving Internal Persistent Objects
Deleting Internal Persistent Objects
Dead Persistent Objects

Making an Object Persistent

Only instances of persistence-capable classes can be persistent objects. Each application defines its own persistence-capable classes. In addition, Objectivity for Java includes persistence-capable classes for collections of persistent objects.

A persistent object can be:

If you perform any of these operations on a transient object, that object is made persistent. If you attempt to perform one of these operations on an object whose class is not persistence-capable, a NonPersistentClassException is thrown.

Container classes are themselves persistence-capable. A container is both a storage object and a persistent object; you can name a container, reference a container in a persistent field of another persistent object, add a container to a persistent collection, and establish a relationship from a persistent object to a container. You can define your own container classes if you need to create containers that have persistent data or relationships; however, most applications have no need to define their own container classes.

When you use the new operator to create an object of a persistence-capable class, the newly created object is transient. On the other hand, when you create an object by copying (using the persistent operation copy, not the Java clone method), an existing persistent object, the new copy is made persistent automatically. This section explains how to make a transient object persistent; "Copying a Persistent Object" explains how to copy an object, creating a new object that is automatically persistent.

You can make a transient object persistent only while a session is in a transaction; the newly persistent object belongs to that session. When you make an object persistent, the corresponding persistent object is created in the federated database. When you commit or checkpoint the transaction, the object is made visible to other clients. If you abort the transaction, the object in memory reverts to being transient and the new persistent object is removed from the federated database.

Immediate and Delayed Persistence

There are many ways to cause a transient object to be made persistent. Some actions make an object persistent immediately; others cause it to be made persistent when you commit or checkpoint the transaction.

Immediate Persistence

Any of the following actions will make a transient object become persistent immediately:

Delayed Persistence

You can also make a transient object persistent by referencing it in a persistent field of a persistent object. The referenced object is not made persistent immediately: it only becomes persistent when the referencing object is written to a database. The referencing object is written to a database when you commit or checkpoint the transaction in which you made the object persistent or modified the object's persistent fields. Thus a transient object referenced by a persistent object is made persistent when you commit or checkpoint the transaction in which you created the reference between the persistent and transient object. This process is repeated recursively; if the referenced object references another transient object, that object is also made persistent, and so on. This process is called persistence by reachability. The operations fd.flush, db.flush, cont.flush, and obj.write will also write out any modified objects graph, hence making transient objects persistent.

Assignment of Storage Location

Making an object persistent assigns it to a storage location in the federated database. Making a basic object persistent assigns it a location in a particular container; making a container persistent assigns it a location in a particular database.

The assignment of a basic object to a container, or a container to a database, can be either explicit or implicit.

For guidelines on deciding where to store your persistent objects, see "Assigning Objects to Databases" and "Assigning Basic Objects to Containers".

Storing an Object Persistently

The following figure illustrates what happens to a basic object's persistent data during the transaction that makes it persistent. A transient object can be created either during a transaction or outside a transaction; in this example the object is created before the start of the transaction that makes it persistent. When an object becomes persistent, the database is informed which container the new object has been assigned to. The database locks that container for read/write access by the session that owns the newly persistent object. Note, however, that the object's persistent data (that is, the values in its persistent fields) is not written to the database. You can think of the database as having an empty object in the specified container.

A new persistent object is first written to the database when you commit or checkpoint the transaction. If the transaction is aborted before the object is written to the database, the corresponding empty object is removed from the database and the object in memory becomes transient again.

Committing a transaction writes the object's persistent data to the database and releases all the session's locks. The object in memory continues to be the session's local representation of the persistent object. The persistent object itself resides in the database and can be shared by other clients, including other sessions of the same application and other applications written in Java, C++, or Smalltalk. Each client has its own local representation of the persistent object.

After the transaction is committed, its session's local representation of a persistent object is not guaranteed to have the same persistent data as the corresponding object stored in the database. A different client may modify the object while the original session is outside a transaction, making the original session's local representation of the object obsolete.


Note: To ensure that you have the most current persistent data for an object, you must start a transaction, then fetch the object's data. See "Fetching an Object's Data".

Working With a Persistent Object

A session can work with a persistent object once all the following conditions have been satisfied:

If a particular operation does not require access to the object's data (the actual values of its field members), you need not fetch the data. In Objectivity for Java, no persistent operations require doing a fetch. For example, deleting, binding, looking up scope names, linking, and moving do not require a fetch prior to execution.
You need to fetch data of objects of application-defined classes only. Remember that an object's persistent data is the values in its persistent fields, and persistent fields are always defined by the application. A class that is part of the Objectivity for Java interface (such as ooMap or ooContObj) has no persistent fields, so objects of those classes do not have persistent data and hence never require fetch() or markModified().

Once these four conditions are met, you can perform any operation that is consistent with the session's lock on the object. For example, if the object is locked for read, you can look at its persistent data or copy it. If the object is locked for write, you can modify, move, or delete it. Any deletions or changes to an object's persistent data exist only in the application's memory until you commit or checkpoint the transaction; then the changes are written to the database.

When you commit or abort a transaction, the session releases all locks obtained during the transaction. The local representation is still owned by the session, and can be reused in subsequent transactions. Note, however, that if another client has deleted the object while your application was between transactions, an exception is thrown the next time you try to perform an operation on the object, pass the object as an argument to other persistent operations, or try to write it out either directly or from a retrieve from another persistent object.

When a transaction ends, all persistent objects that were made persistent or whose data was fetched during the transaction are marked as needing to have their data fetched. As long as the session is outside a transaction, those objects are not guaranteed to have persistent data consistent with the database, because a different client may have modified them. To ensure that you have access to the most recent version of an object's persistent data, you must start a transaction and fetch the object's data again.

If your application uses multiple session objects, remember that each persistent object, storage object, and ODMG transaction object in the application's memory belongs to a particular session. A newly created persistent object belongs to the session that was in a transaction when it was made persistent. A previously existing persistent object belongs to the session that was in a transaction when the object was retrieved from the database.

Objectivity for Java does not allow a persistent object that belongs to one session to interact with objects that belong to another session. For example, if Session1 owns a particular database and Session2 owns a particular basic object, you may not make that basic object a named root in that database. Instead, you must retrieve the same basic object while Session2 is in a transaction. You can then make the newly retrieved basic object a named root in the database because both the newly retrieved object and the database belong to Session2. For additional information, see "Object Isolation".

Retrieving an Object From the Database

When you retrieve a persistent object from a database, Objectivity for Java creates an object in the application's memory that represents the object stored in the database. The newly created object has no persistent data; you must explicitly fetch the object's data from the federated database to the local representation. Once you have done so, you can access the data in the object's persistent fields just as you would access the fields of any Java object.

Although the object in memory is called a "persistent object," it is important to remember that the persistent object actually resides in the database. The object in memory is just the session's local representation of the persistent object.

Several methods allow you to retrieve objects from the database. Some of these methods retrieve an individual object, for example, by looking up its root name or its name in the scope of a particular scope object. Other methods return an iterator that finds a number of similar objects, for example, all basic objects of a given class that are stored in a given container. For more information about these methods, see Chapter 11, "Retrieving Persistent Objects".

Every session maintains a cache of the persistent objects that belong to it (including internal persistent objects). The cache ensures that the session has a single object in Java memory representing any particular persistent object that is accessed while the session is in a transaction. When the session is in a transaction, if two different method calls look up the same persistent object, the first method call retrieves the object from the database and returns a local representation for it; the second call returns the local representation created by the first call. For additional information, see "Object Identity".

A particular object remains in the cache until any of the following actions occur:

When multiple processes run concurrently and access the same objects, there is a possibility that the cached information will become stale if you hold a reference for an object after committing or aborting the transaction in which it was retrieved. See "Avoiding Stale Cache Information".

Locking a Persistent Object

Locking an object informs Objectivity/DB how you plan to use that object. The lock you obtain while one session is in a transaction prevents other sessions and other applications from taking actions that would interfere with your intended use of the object.

A session can lock a persistent object either for read-only access or for read/write access.

Containers are the fundamental unit of locking; when a session locks a basic object, the session implicitly obtains a lock on the object's container. When a session locks a container, the session implicitly obtains locks on all basic objects in the container.

See Chapter 4, "Locking and Concurrency," for a description of MROW and non-MROW transactions and for further details about locking.

Obtaining a Lock

You usually don't need to make an explicit call to lock a persistent object. Built-in methods that require a persistent object to be locked will try to obtain the necessary lock. For example, you cannot fetch an object's persistent data unless the session has a read lock on the object; the fetch method obtains the necessary lock before it fetches the data.

If a method is unable to obtain a lock, it will throw a LockNotGrantedException. You can reserve locks in advance of when you need them with the lock method.

If an object's class has field access methods to ensure that its persistent fields are used safely, the methods to get and set the value of a persistent field will automatically lock the object for you. See "Field Access Methods".

Once your session obtains a lock, it typically retains the lock until the transaction is committed or aborted.

Modifying a Lock

After you obtain a lock, you can modify it in the following ways during the transaction:


Warning: Objectivity/DB does not prevent you from changing the persistent fields of an object you have locked for read-only access. It is your responsibility to access an object consistently with the lock you have obtained for it.

Fetching an Object's Data

Fetching an object's data locks the object, if necessary, and transfers the object's persistent data from the federated database to the persistent fields of the local representation. Once you have fetched an object's data, you can access the data in the object's persistent fields just as you would access the fields of any Java object. If a persistent field references a persistent object that has not already been retrieved, fetching the field's data retrieves the referenced object but does not fetch that object's data; thus, the field is set to reference an empty object. A referenced object's data must be fetched independently.

Once you fetch an object's data, it typically remains locked with its data up to date until the transaction is either committed or aborted.

You must call an object's fetch method to fetch its data; Objectivity/DB never automatically fetches persistent data. However, if the object's class has field access methods to ensure that its persistent fields are used safely, the method to get the value of a persistent field will fetch the object's data; see "Field Access Methods". There is no performance overhead for making multiple calls to an object's fetch method; if the data in memory is up to date, the method does nothing.


Note: Objectivity/DB does not prevent you from accessing the persistent fields of an object that is marked as needing to have its data fetched; however, the data in those fields is not guaranteed to be consistent with the database.

Modifying a Persistent Object

Before you change the persistent data of a persistent object, you should obtain a write lock on the object and fetch its data. To do so, you should call the object's markModified method; in addition to locking the object's container and fetching its data, this method marks the object as modified. When you commit or checkpoint the transaction, any persistent objects that have been marked as modified will be written to the database; your changes to the object are then available to other processes.

If the object's class has field access methods to ensure that its persistent fields are used safely, the method to set the value of a persistent field will call markModified for you. See "Field Access Methods".


Warning: If an object is an element in a sorted set or the key of an element in a sorted object map, you must remove the element from the collection before making any change to the object that would affect how the element is sorted. Similarly, if an object is an element in an unordered set or the key of an element in an unordered object map, you must remove the element from the collection before making any change to the object that would affect how the element's value is computed. See Chapter 9, "Persistent Collections".

The following figure illustrates what happens to a basic object's data during a transaction that modifies it. Any changes to an object's persistent data exist only in the application's memory until you commit or checkpoint the transaction; then the changes are written to the database.

Copying a Persistent Object

To create a new persistent object with the same persistent data as an existing persistent object, call the copy method of the existing object. You can copy basic objects only, not containers.

If the object you are copying is marked modified, it is written to the federated database (but is not committed) before the copy operation begins. The operation is performed in the federated database, not in memory. That is, a new persistent object is created in the federated database; the copy method specifies the database, container, or basic object with which to cluster the new object. Persistent data is copied from the existing object in the federated database to the new object in the federated database; see "Copying the Object's Fields". Relationships of the existing object are copied or not, as specified by the definition of each relationship; see "Copying the Object's Relationships". The copy method returns a local representation of the new object; the returned object is persistent, locked for write, and empty. You must fetch its persistent data if you want to examine or modify that data.

When you commit or checkpoint the transaction in which you copy an object, the new copy is made visible to other clients. If you abort the transaction, the new copy is removed from the federated database and the new copy in memory becomes a dead object.


Note: The Objectivity for Java copy operation is not the same as the Java clone operation. The following sections describe the copy operation in more detail.

Copying the Object's Fields

The copy method copies the persistent object stored in the database rather than the session's local representation of that object. In fact, the local representation of the original object does not need to have its persistent data in memory. Because the database contains only persistent data, any transient fields are not affected by the copy operation.

The copy method creates a shallow copy of the original persistent object. Each field of a shallow copy contains exactly the same value as the corresponding field of the original object. If the original object contains a reference to an array or an object in a persistent field, the shallow copy contains a reference to the same array or object in its corresponding field. In contrast, each field of a deep copy contains a copy of the value in the corresponding field of the original object. If the original object contains a reference to an array or an object in a persistent field, the deep copy contains a reference to a deep copy of the array or object in its corresponding field.

Your application may need to implement a mechanism for copying a persistent object with its transient data or for creating a deep copy of a persistent object. If so, you should implement your customized copy mechanism as follows:

  1. Call the copy method of the original persistent object to create a new persistent object that is a shallow copy of the original object.

  2. Fetch the new object's persistent data and modify its fields as appropriate for your application. For example, copy the values of the transient fields and/or replace the object references in persistent fields with references to deep copies of the currently referenced objects.

  3. If your copy mechanism modifies any persistent fields of the new copy, it must call the new copy's markModified method to ensure that the changes are written to the database when you commit or checkpoint the transaction. (If the object's class has field access methods to ensure that its persistent fields are used safely, the method to set the value of a persistent field will mark the object as modified for you; see "Relationship Access Methods".)

Copying the Object's Relationships

If the object being copied has relationships to other objects, the definition of each relationship specifies how the relationship is handled. When a relationship is created, its copy behavior is specified with one of the following constants (defined in the Relationship class):

The following figure illustrates the result of copying an object with a relationship using each of the three copy modes.

Moving a Persistent Object

At some point you may decide that you need to reallocate objects to a different container configuration. For example, you may want to increase the number of containers to achieve greater concurrency. Objectivity for Java supports moving a persistent basic object to a new container with the move method.

The move method requires that you specify the database, container, or basic object that you want the moved object to be near. The following table summarizes where a basic object is stored for any given near object.

Near ObjectWhere the Moved Object is Stored
DatabaseDefault container of the database.
ContainerThat container.
Container currently containing the basic objectThat container; the basic object may be moved to another location within the container.
Basic objectContainer in which that basic object is stored; on the same page as the specified object, if possible, or on a page close to that object.

After a successful move, the object has a new object identifier, and the local representation of the object references the new basic object. If the move is unsuccessful, the identifier is unchanged and the local representation remains valid and still references the original object.


Note: Moving an object moves that object alone, not any objects referenced by the object's fields. If you want to move a graph of objects, you must traverse the graph and explicitly move each object. In addition, you may explicitly move any referenced internal persistent objects.

If you abort the session after moving an object:


Warning: Since moving an object changes its object identifier, all Objectivity/DB-maintained references to the object containing the old identifier become invalid and any attempt to access such a reference will cause an ObjectNotFoundException. Furthermore, the old identifier may eventually be reassigned to a new persistent object by Objectivity/DB. Therefore, when you move an object, you should, within the same transaction, update references to the object within all relevant relationships, persistent collections (including root named maps), scopes, and indexes. Failure to do so may result in inconsistent references and database corruption.

Object Linked by Relationship

If the moved object is referenced from a unidirectional relationship of another object, you must drop a to-one relationship or remove a to-many relationship before the move and then form a to-one relationship or add a to-many relationship after the move. For information on how to work with relationships, see "Accessing Relationships". On the other hand, if the moved object has bidirectional relationships with other objects, Objectivity/DB updates any references to the object.

Object in a Persistent Collection

You should not move an object that is used in a persistent collection:

If you need to move such an object, you must first temporarily remove the appropriate element(s) from all affected persistent collection(s). After the move is successfully completed, you can add the removed element(s) back to the collection(s).


Warning: If an object in a persistent collection is first moved and then deleted, data corruption or data loss to the collection's objects could result. To avoid data corruption, you must remove an object from a collection before moving the object.

Object Used as a Named Root

Objectivity for Java uses name maps to store root names. As a consequence, the recommendations and warnings concerning moving objects referenced in a persistent collection apply also to named roots. To prevent possible corruption of a root dictionary and any objects it references, you must unbind a root name before you move the object, and bind the object after the move. See "Named Roots" for information on how to work with named roots.

Scope Object or Scope-Named Object

An object that is a scope object or is scoped by other objects will lose that scope when the object is moved. This will not compromise data integrity in any way. However, any moved object will need to reestablish scope-to or scope-by any objects after the move.

To preserve the moved object as a name scope, you must:

  1. Before the object is moved, call scopedObjects to determine all the scope-named objects.

  2. Before the object is moved, call lookupObjName to determine the scope name of each scope-named object.

  3. After the object is moved, reestablish the scope name of each scope-named object in the scope of the moved object with the nameObj method.

To preserve all the scope names of a moved object you must:

  1. Before the object is moved, determine the scope objects of the object by calling the scopedBy method.

  2. Before the object is moved, determine the scope name of the object in each scope, by calling the scope object's lookupObjName method.

  3. After the object is moved, reestablish the scope name of the moved object in each scope by calling the scope object's nameObj method.

See "Name Scopes" for further information on how to work with scope named objects.

Indexed Object

A predicate scan using an index that references a moved object will yield undefined results. In contrast to relationships, name maps, and scopes, it is not possible to delete a reference to an individual object from an index. Therefore, you must delete the entire index before moving an object that is referenced by the index and recreate the index after the object is moved. See "Working With an Index" for information on how to delete and recreate an index.

Deleting a Persistent Object

Deleting a persistent object removes the object from the database when the transaction commits. Deleting a persistent object deletes any association links from the deleted object to destination objects. Furthermore, if any of the associations is bidirectional, the inverse link to the deleted object is removed from each destination object to maintain referential integrity. However, if another persistent object references the deleted object through a unidirectional association or directly in one of its attribute data members, you are responsible for removing that reference.

To delete a basic object, retrieve the object from the database and call its delete or deleteNoProp method. To delete a container and all its objects, get a local representation of the container, and call its delete or deleteNoProp method.

The delete methods propagate the deletion operation along relationships for which deletion propagation is enabled.

When you use the delete method to delete a persistent object that has relationships for which delete propagation is enabled, you also delete the destination objects linked by those relationships. If you want to delete a source object without deleting its destination objects, you should call its deleteNoProp method instead.

The session's local representations of deleted objects are converted to dead objects. When you commit or checkpoint the transaction, the objects are physically removed from the federated database. If the transaction is aborted, the objects are not removed from the federated database.

The following figure illustrates a transaction in which a basic object is deleted.

Deleting an Object with References

Deleting an object deletes that object alone, not any objects referenced by the object's fields. If the object is stored in a garbage-collectible container, its referenced objects will become available for garbage collection if no other object references them. If you store a graph of related objects in a non-garbage-collectible container, however, you must take explicit action if you want to delete the entire graph of objects. You must traverse the graph and explicitly delete each object. In addition, you should explicitly delete any referenced internal persistent objects. "Container Types" explains the difference between garbage-collectible and non-garbage-collectible containers.

Deleting an Object with Relationships

A deleted object is removed from any bidirectional relationships in which it is involved. However, if another persistent object references the deleted object in a unidirectional relationship or directly in one of its persistent fields, you are responsible for removing that reference. An exception is thrown if you attempt to write a persistent object that references a dead object.

Deleting an Object in a Persistent Collection

You should not delete an object used in a persistent collection:

If you need to delete such an object, you must first remove the appropriate element(s) from all affected persistent collections.

Avoiding Stale Cache Information

Each session maintains a cache of the objects that belong to it. You typically do not need to be concerned about the state of the information in the cache. In a concurrent environment, however, multiple processes may run simultaneously, each creating, moving, and deleting objects of the same class. Situations can arise in which the actions of one process render the cache in another process "stale."

Within the cache, objects are organized by their object identifiers (OIDs). Any method that looks up a persistent object gets the object's OID from the federated database and checks the session's cache for that OID. If the OID is not in the cache, the persistent object is retrieved from the database; otherwise the persistent object is obtained directly from the cache.


Note: If your application runs in a multiprocessing environment in which other processes may delete or move objects that your application accesses, you should release all references to a persistent object at the end of the transaction in which the object was made persistent or retrieved. Doing so allows the Java garbage collector to remove the object from the cache so that your application will retrieve it anew if it is needed in a different transaction on the same session.

At the end of the transaction in which a persistent object is retrieved from the database, your application gives up its lock on the object, permitting other processes to access the object. If your application holds a reference to the object, the Java garbage collector cannot remove the object's data from the session's cache. If another process then moves or deletes the object, the cached information becomes stale. The cache continues to identify the object by its original OID, which is now invalid:

The situation is complicated by the fact that Objectivity/DB reuses OIDs that have become invalid. If an invalid OID in the cache is reassigned to a different object, your application will use the OID thinking that it identifies one object when it actually identifies a different object--possibly an object of a different class. For example, if your application performs an operation that obtains the reassigned OID, it will not retrieve the correct object from the database but will instead use the cached object.

If the OID has been reassigned to an object of a different class, the cached type number for the OID will identify the class of the deleted or moved object, whereas the type number for the OID in the database will identify the class of the object to which the OID is now assigned.

Three problems can arise when you access an object with stale cache information:

Unassigned OID

If you try to access an object whose OID is now unassigned, an ObjyRuntimeException is thrown, indicating that the object does not exist in the database.

If you detect this situation, you can remove the stale cached information in either of two ways:

OID Reassigned to Object of Same Class

If the OID for a moved or deleted object is assigned to a different object of the same class, your application may use the cached object inappropriately instead of retrieving the new object. Although there is no way to detect that an object's OID has been assigned to a different object of the same class, your application may find that the object's data appears inconsistent.

Objectivity/DB protects against database corruption in the case that you make changes to the cached object and try to save it. If you attempt to write the object whose cached information is stale, its data is fetched first, which will bring the correct data into the Java object. If the object was moved or deleted and its OID reassigned, you will write the new object, thinking that it is writing the old (deleted or moved) object.

The same problem arises if an object X has a field that references an object Y and Y's OID is reassigned to an object of the same class; X will still have a valid reference, but to the wrong object.

Because these problems are undetectable, if your application runs in a multiprocessing environment in which applications may be moving or deleting objects, the safest programming practice is to drop all object references at the end of every transaction.

OID Reassigned to Object of Different Class

If the OID for a moved or deleted object is assigned to a different object of a different class, your application may use the cached object inappropriately instead of retrieving the new object. In this case, two exceptions may be thrown:

A ClassCastException doesn't always indicate that your application is using a persistent object with stale cache information. This exception is also thrown in other, unrelated, cases in which you try to cast a Java object to an illegal type.
This exception is also thrown if you try to get a referenced or related object whose cached type number does not match the type number in the database.

If you detect that a particular object has stale cached information, you have two alternatives. You can reload the cache, obtaining a reference to the new object that uses the old object's OID, or you can drop the object's cached information.

The decision whether to get a new object reference or drop the cached information depends on the specifics of your application. For example, suppose you perform a scan operation that retrieves an object that does not match its cached information. In that case, you should reload the cache so that you can get the new object that matches the scan criteria. On the other hand, suppose that object X references object Y. If Y has been deleted by another process and it's OID reused by an object of a different class, you should drop the cached information for Y and remove X's reference to the new object.

Reloading the Cache

You can reload the object calling the reloadCachedObject method of the object's session. This method removes the old object's cached information, making it a dead object; it then retrieves and returns the new persistent object that uses the old object's OID and caches up-to-date information about the new object.

Example

This transaction gets all objects from a particular container and tries to cast each one to the SimpleClassWithField class. If the cast fails, the cache has stale information about an object of a different class that used to have the same OID. In that case, the code reloads the object and casts it to SimpleClassWithField.

    session.begin();
    System.out.println("getting iterator...");
    Iterator itr = cont.contains();
    SimpleClassWithField scwf = null;
    int count = 0;
    while (itr.hasNext()) {
        Object o = itr.next(); // Retrieve generic Object
        try {
            scwf = (SimpleClassWithField) o; // Try the cast
        }
        catch (ClassCastException e) {
            // Cast didn't work, reload the object
            scwf = 
                (SimpleClassWithField) session.reloadCachedObject(o);
        }
        System.out.println("Got " + scwf.toString());
        count++;
    }
    System.out.println("count of objects: " + count);
    session.commit();
    
Dropping the Object

You can drop the object's cached information and make it a dead object by calling the dropCachedObject method of the object's session.

Example

This transaction scans for all objects of the SimpleClassWithRef class. It calls the getRef method of each retrieved object to get a referenced object, which should be an instance of SimpleClass. If the referenced object is not of the correct type, it drops that object's cached information and calls the referencing object's setRef method to remove the reference to the deleted or moved object.

    session.begin();
    System.out.println("getting iterator...");
    Iterator itr = 
        cont.scan("test.data.SimpleClassWithRef");
    Object o = null;
    SimpleClassWithRef scwr = null;
    SimpleClass sc = null;
    boolean needToCheckRefType = false;
    int count = 0;
    while (itr.hasNext()) {
        scwr = (SimpleClassWithRef) itr.next(); 
        System.out.println("fetching " + scwr.toString());
        scwr.fetch();
        sc = scwr.getRef();
        try {
            sc.fetch(); 
        }
        catch (JavaTypeNMismatchException e) {
            // Get the referenced object as type Object
                o = session.reloadCachedObject(sc); 
                needToCheckRefType = true;
        }
        if (needToCheckRefType) {
            // Got a JavaTypeNMismatchException
            needToCheckRefType = false;
            try {
                sc = (SimpleClass) o; 
            }
            catch (ClassCastException e) {
                // This reference is bad
                System.out.println(
                    "ClassCastException: " +
                    e.getMessage() + 
                    ", setting reference to null");
                // Remove the reference
                scwr.setRef(null); 
                // Remove sc from the cache
                session.dropCachedObject(sc);
            }
        } // If need to check
        count++;
    } // While more objects
    System.out.println("count of objects: " + count);
    session.commit();
    

Removing Suspect Cached Data

In addition to dropping or reloading cached information for a particular object, you can drop all objects of a particular class from every session's cache or you can drop all objects that belong to a particular session.

If your application detects repeated problems with its cached data, you can try either of the following corrections:

If you want to continue to retrieve or create objects of the class, you must ensure that the schema class description is restored. If the dropped class uses a custom schema class name, after dropping it, you must call the connection object's setSchemaClassName method to reset the schema class name, then call the connection object's registerClass method to retrieve the schema class description from the federated database to the cache.

If problems do not appear to be localized to a small number of classes or a small number of sessions, the best approach is to terminate all sessions in your application and create one or more new sessions for future interactions with the federated database.

Internal Persistent Objects

When you store a persistent object containing a non-null field of one of the following Java types, an internal persistent object for that field is created in the same container:

        java.util.Date
        java.sql.Date
        java.sql.Time
        java.sql.Timestamp

Furthermore, an internal persistent object is created for each element of an array of the following types:

        String[]
        java.util.Date[]
        java.sql.Date[]
        java.sql.Time[]
        java.sql.Timestamp[]


Note: A String field is not stored as an internal persistent object; however, the elements of a String[] array are stored as internal persistent objects.

You generally do not need to deal directly with these internal persistent objects, but you need to be aware of their existence in the following circumstances:

Moving Internal Persistent Objects

If a class has fields that reference internal persistent objects, you can override the move method to move the internal objects along with the referencing object. If move than one object can reference the same internal persistent object, however, you may not want to override move.

Your move method should move:

Example

The Appointment class has three fields that contain, respectively, a date, an array of times, and a string. This class overrides the move method to move any internal objects referenced by the moved object. Note that the String field announcement does not need to be moved because it does not reference an internal persistent object.

    public class Appointment extents ooObj {
        private java.util.Date scheduledDate;
        private java.sql.Time availableTimes[];
        private String announcement;
        ...
        public void move(Object o) {
            moveReferences(o);
            super.move(o);
        }
    
        private void moveReferences(Object o) {
            markModified();
            com.objy.db.app.ooFDObj fd = getSession().getFD();
    
            // Move date referenced by shecheduledDate field
            fd.moveReference(scheduledDate, o);
                
            // Move each time in the availableTimes array
            for (int i=0; i<3; i++) 
                fd.moveReference(availableTimes[i], o);
    
            // Move the availableTimes array
            fd.moveReference(availableTimes, o);            
        }
    }
    

Deleting Internal Persistent Objects

If you always store objects of your classes in garbage-collectible containers, you do not need to delete internal persistent objects. When a given internal persistent object cannot be reached from a named root, it will be available for garbage collection. On the other hand, if objects that reference internal persistent objects may be stored in non-garbage-collectible containers, you should take care to delete the internal persistent objects when they are no longer needed. "Container Types" explains the difference between garbage-collectible and non-garbage-collectible containers.

For example, if objects of your class may be stored in non-garbage-collectible containers, and you know that a given internal persistent object can be referenced by at most one other object, you can override the delete and deleteNoProp methods to delete the internal objects along with the referencing object. Your methods should delete:

Example

The Product class has three fields that contain, respectively, a time stamp, an array of long integers, and an array of strings. This class overrides the delete and deleteNoProp methods to delete any internal objects referenced by the deleted object. Note that the elements of the long[] array in the repairCodes field do not need to be deleted, but elements in the String[] array in the notices field do.

    public class Product extents ooObj {
        private java.sql.Timestamp lastUpdated;
        private long repairCodes[];
        private String notices[];
        ...
        public void delete() {
            deleteReferences();
            super.delete();
        }
    
        public void deleteNoProp() {
            deleteReferences();
            super.deleteNoProp();
        }
    
        private void deleteReferences() {
            markModified();
            com.objy.db.app.ooFDObj fd = getSession().getFD();
    
            // Delete timestamp referenced by lastUpdated field
            deleteReference(lastUpdated);
    
            // Delete the repairCodes array
            deleteReference(notices);            
    
            // Delete each string in the notices array
            for (int i=0; i<3; i++) {
                deleteReference(notices[i]);
                notices[i] = null;
            }
    
            // Delete the notices array
            deleteReference(notices);            
        }
    }
    

Dead Persistent Objects

A dead object is an object that is no longer valid for Objectivity for Java operations. A persistent object becomes dead when it is deleted or when the session that owns it is terminated.

If the transaction is aborted, an object that was made persistent during the transaction goes back to the transient state; an object that was retrieved remains a dead object. If the object's database was deleted, the object no longer exists in the federated database; however, if the object itself (or its container) was deleted, the object continues to exist in the federated database after the transaction is aborted. The local representation of the object is a dead object, but you may retrieve the object again if you need to work with it.

When a persistent object becomes a dead object, it loses any relationships it had to other objects.

Any operation that you would normally perform on a persistent object is invalid on a dead object; if you attempt such an operation, an ObjectIsDeadException will be thrown. You can call an object's isDead method to test whether it is dead.

After a session is terminated, all the objects that belong to the session will behave like dead objects. Although isDead will return false for these objects, an ObjectIsDeadException will be thrown if you attempt to operate on them.



Guide  Reference  Contents  Previous  Next  Index



Copyright © 2000, Objectivity, Inc. All rights reserved.