Guide  Reference  Contents  Previous  Next  Index



Retrieving Persistent Objects

Objectivity/DB provides a variety of mechanisms for retrieving objects from a federated database. You can retrieve individual objects based on their application-defined names and links with other objects. In addition, you can search storage objects for objects of a given class, possibly restricting the search based on the values of certain persistent fields. Finally, you can traverse the entire storage hierarchy, retrieving all objects within each storage object.

In This Chapter

General Guidelines
Looking Up an Object by Name
Root Name
Scope Name
Name in an Application-Defined Dictionary
Finding Objects in a Graph
Persistent Fields
Relationships
Retrieving Elements of a Persistent Collection
Collection of Objects
Collection of Key-Value Pairs
Scanning Storage Objects
All Objects of a Class
Objects of a Class that Satisfy a Condition
Traversing the Storage Hierarchy
Looking Up an Object by OID

General Guidelines

The following general guidelines apply to retrieval methods.

The return type of every retrieval method is Object; you should cast the retrieved object to the appropriate class before using it.


Note: Many retrieval methods do not lock the retrieved object or fetch its persistent data. Certain methods take a parameter that specifies the lock mode for the retrieved object; those methods lock the object as indicated but do not fetch its data.

Looking Up an Object by Name

The most direct way to retrieve an object is to look it up by name. Of course, this assumes that the object has been given a name, as described in Chapter 10, "Naming Persistent Objects".

Root Name

To retrieve an object with a particular root name, call the lookup method of the federated database or database in which it is a named root. The session that owns the local representation of the federated database or database must be in a transaction.

The lookup method takes the root name as its parameter and returns the object with that root name. The session must be able to obtain a read lock on the root dictionary of the federated database or database. The lookup method throws a checked exception (ObjectNameNotFoundException) if there is no object with the specified name.

Example

This code fragment retrieves an object of the Salesperson class by looking up its root name in the database salesDB. The complete method definition appears in the Sales.Interact programming example . The Interact class has a static field called session, which is initialized to contain an instance of the Session class when the connection is opened; all examples in this chapter use session for transaction control.

    // Static utility to retrieve a salesperson
    public static Salesperson lookupSalesperson (...) {
        Salesperson salesperson;
        ...
        session.begin();
        ...
        ooDBObj salesDB = ...;
        ...
        String rootname = String.valueOf(employeeID);
        // Look up the salesperson
        try {
            salesperson = (Salesperson) salesDB.lookup(rootname);
        } catch (ObjectNameNotFoundException e) {
            System.out.println("No salesperson with Employee ID " +
                               rootname);
            session.abort();
            return null;
        }
        session.commit();
        ...
    }

Scope Name

To look up an object by its scope name, call the lookupObj method of the scope object. The session that owns the local representation of the scope object must be in a transaction.

The lookupObj method takes the scope name as its parameter and returns the object with that scope name. The session must be able to obtain a read lock on the scope object's name map. A runtime error is thrown if lookupObj fails to find the object; for example, because there is no object with the specified name, a runtime error is thrown.

Example

This code fragment retrieves an object of the Contact class by looking up its name in the scope of the container scope. The complete method definition appears in the Sales.Interact programming example .

    // Static utility to retrieve a contact
    public static Contact lookupContact (...) {
        Contact contact;
        ...
        session.begin();
        ...
        String scopename = ...;
        ooContObj scope = ...;
        try {
            contact = (Contact) scope.lookupObj(scopename);
        } catch (ObjyRuntimeException e) {
            System.out.println("No contact named " + scopename);
            session.abort();
            return null;
        }
        ...
        session.commit();
        ...
    }

Name in an Application-Defined Dictionary

To look up an object by its name in an application-defined dictionary:

  1. First retrieve the name map representing the dictionary.

  2. Next, call the name map's isMember method to check whether the name map contains an element with the specified name.

  3. If the name exists, call the name map's lookup method.

The session must be able to obtain a read lock on the name map.

The lookup method takes the name as its parameter and returns the object in the name map whose key is the specified name. You should call lookup only if isMember returns true; lookup throws an ObjyRuntimeException if you try to look up a name that does not exist in the name map.

Example

This code fragment retrieves an object of the Client class by looking up its name in the application-defined dictionary represented by the name map nameTable. The complete method definition appears in the Sales.Interact programming example .

    // Static utility to retrieve a client
    public static Client lookupClient (String companyName) {
        ooMap nameTable;
        ...
        session.begin();
        ...
        nameTable = ...;
        // Look up name in table
        ...
        if (nameTable.isMember(companyName)) {
            Client client = (Client)nameTable.lookup(companyName);
            session.commit();
            return client;
        }
        else {
            System.out.println("No client named " + companyName);
            session.abort();
            return null;
        }
    }

Finding Objects in a Graph

An object graph is a group of related persistent objects that are linked together in a directed graph data structure. Each link in the graph can be a persistent field that references another object or a relationship. Once you have retrieved one object in the graph (for example, a named root), you can follow links to retrieve the other objects in the graph.

Persistent Fields

When you fetch a retrieved object's data, you retrieve any objects the retrieved object references in its persistent fields. Note however, that the referenced objects are empty; you should fetch their persistent data before you access their persistent fields.

If you define field access methods for every class whose objects are included in an object graph, the process of fetching persistent data and retrieving referenced objects is completely transparent. The access methods that get the values of persistent fields also fetch the data for you, retrieving any referenced objects after obtaining the necessary locks. Remember that you should call a persistent object's field access methods only when the session that owns the object is in a transaction.

Example

This code fragment first retrieves an object of the Client class; the lookupClient method starts its own transaction, so that method is called outside a transaction. Once the client is retrieved, a call to its getSalesRep field access method gets the client's sales representative, an object of the Salesperson class. When getSalesRep (shown at the end of the example) calls fetch, the client's data (including the empty salesperson object) is retrieved from the database. The complete printClientRepresentative method definition appears in the Sales.Interact programming example .

    // Static utility to print the name of a client company's 
    // sales representative
    public static void printClientRepresentative(String companyName) 
{
        ...
        Client client = Interact.lookupClient(companyName);
        ...
        session.begin();
        // Get the client's sales representative
        Salesperson salesPerson = client.getSalesRep();
        ...
        session.commit();

        public Salesperson getSalesRep() {
            fetch();
            return this.salesRep;
    }

If a persistent field contains a persistent collection rather than a scalar persistent object, you can retrieve the elements of the collection as described in "Retrieving Elements of a Persistent Collection".

Relationships

After you retrieve an object, you must fetch its persistent data to read its relationship field into memory. If you try to access the object's relationship field before you have fetched its data, a NullPointerException is thrown.

If you define relationship access methods for each relationship, the process of fetching persistent data and retrieving related objects is completely transparent. The access method for a relationship fetches the object's data, retrieves the related object, and casts the related object to the correct class. Remember that you should call a persistent object's relationship access methods only when the session that owns the object is in a transaction.

To-One Relationships

You can retrieve an object related by a to-one relationship by calling that relationship's get method. The get method takes no parameters and returns the related object, or null if there is no related object.

Example

This code fragment first retrieves an object of the Contact class; the lookupContact method starts its own transaction, so that method is called outside a transaction. Once the contact is retrieved, a call to its getSalesperson relationship access method gets the contact's salesperson, an object of the Salesperson class. When getSalesperson (shown at the end of the example) calls fetch, the contact's data, including its salesperson relationship, is retrieved from the database. The complete printSalespersonForContact method definition appears in the Sales.Interact programming example .

    // Static utility to print the name of a contact's salesperson
    public static void printSalespersonForContact (...) {
        Contact contact = Interact.lookupContact(...);
        ...
        session.begin();
        // Get the contact's salesperson
        Salesperson salesPerson = contact.getSalesperson();
        ...
        session.commit();

    public Salesperson getSalesperson() {
        fetch();
        // Cast retrieved object to class Salesperson
        return (Salesperson)this.salesperson.get();
    }

To-Many Relationships

You can retrieve objects related by a to-many relationship by calling that relationship's scan method. The scan methods return an iterator initialized to find the related objects.

All Related Objects

When you want to retrieve all related objects, you should call the relationship's scan method with no parameters. The scan method returns an iterator that finds all related objects.

Example

This code fragment first retrieves an object of the Salesperson class; the lookupSalesperson method starts its own transaction, so that method is called outside a transaction. Once the salesperson is retrieved, a call to its getAllContacts relationship access method (shown at the end of the example) gets an iterator that finds all contacts for the salesperson. The iterator is used to retrieve each of the salesperson's contacts, which are objects of the Contact class. The complete printContactsForSalesperson method definition appears in the Sales.Interact programming example.

    // Static utility to print the names of a salesperson's contacts
    public static void printContactsForSalesperson(int employeeID) {
        ...
        Salesperson salesperson = 
                Interact.lookupSalesperson(employeeID);
        ...
        session.begin();
        // Get the salesperson's contacts
        Iterator itr = salesperson.getAllContacts();
        ...
        Contact contact;
        while (itr.hasNext()) {
            contact = (Contact)itr.next(); // Cast to Contact
            contact.printName();
        }
        session.commit();

    public Iterator getAllContacts () {
        fetch();
        return this.contacts.scan();
    }
Related Objects that Satisfy a Condition

When you want to retrieve only those related objects that satisfy some condition, you should call the relationship's scan method, passing the condition as a parameter. The condition is a predicate in the Objectivity/DB predicate query language. The scan method returns an iterator that finds those related objects satisfying the specified condition.

Example

In this example, the findContact relationship access method of the Salesperson class retrieves a particular related contact of a salesperson; the findContactsByCompany access method returns an iterator that finds the related contacts in the specified company.

    // Relationship access methods
    ...
    public Contact findContact(String firstName,
                               String lastName,
                               String company) {
        fetch();
        String predicate = new 
                    String("company == \"\"" + company + "\" and " +
                            "lastName == \"\"" + lastName + "\" and " +
                            "firstName == \"\"" + firstName + "\"");
        Iterator itr = this.contacts.scan(predicate);
        if (itr.hasNext())
            return (Contact)itr.next(); // Cast to Contact
        else 
            return null;

    public Iterator findContactsByCompany(String company) {
        fetch();
        String predicate = new String("company == \"\"" 
                                      + company + "\"");
        return this.contacts.scan(predicate);
    }

Retrieving Elements of a Persistent Collection

Once you have retrieved a persistent collection, you can call its methods to retrieve the elements it contains. Different methods are available for different kinds of collections.

Collection of Objects

Lists, unordered sets, and sorted sets are collections of persistent objects. You can iterate through such a collection, retrieving each object it contains. If the collection is ordered, you can additionally retrieve the element at a particular position within the collection.

Iterating Through the Elements

You can call the iterator method of a collection of objects to obtain an iterator for finding all elements of the collection. For loop control, you call the iterator's hasNext method to test whether additional elements remain. Within the loop, you make successive calls to the iterator's next method to get a local representation of each object in the collection.

You can call the listIterator methods of a list to obtain a list iterator for finding its elements. One version of listIterator returns a list iterator initialized to start at the first element; the other version returns a list iterator initialized to start at a specified index within the list. A list iterator is an instance of a class that implements the java.util.ListIterator interface. In addition to the standard iterator methods, a list iterator has additional methods that allow you to reverse the direction of iteration:

Retrieving Elements by Position

Ordered collections of objects (lists and sorted sets) have methods to retrieve particular elements, identified by their position in the list.

Collection of Key-Value Pairs

Name maps, unordered object maps, and sorted object maps are collections of key-value pairs. You can retrieve the keys and values from such a collection.

Retrieving Keys

You can iterate through a collection of key-value pairs, getting each key it contains. If the collection is ordered, you can additionally retrieve the key at a particular position within the collection.

Iterating Through the Keys

To obtain an iterator for finding all the keys of a collection of key-value pairs:

You call the iterator's next method repeatedly to get each key in the collection. In the case of a name map, next simply returns the key string; in the case of an object map, next gets a local representation of the key object.

Retrieving Keys by Position

Sorted object maps have methods to retrieve particular keys, identified by their position in the list.

Retrieving Values

You can obtain values from a collection of key-value pairs in either of two ways. First, you can iterate through a collection of key-value pairs, retrieving each value it contains. Second, if you have already obtained a key from the collection, you can retrieve the value associated with that key.

Iterating Through the Values

To obtain an iterator for finding all the values of a collection of key-value pairs:

You call the iterator's next method repeatedly to get a local representation of each value in the collection.

Retrieving Values by Key

Once you have obtained a key from a collection of key-value pairs, you can retrieve the object that is the value associated with that key. In the case of a name map, the key is a name. Retrieving the associated value is equivalent to looking up the object by name; see "Name in an Application-Defined Dictionary".

In the case of an object map, the key is another persistent object. You call the object map's get method, passing the key as the parameter.

The get method returns null if the collection does not contain an element with the specified key. To distinguish that situation from the situation in which the collection contains an element with the specified key and a null value, call the containsKey method; the parameter to containsKey is the key.

Scanning Storage Objects

You can scan any storage object to find its contained objects of a particular persistence-capable class. In particular, you can:

If you need to find all containers in a database, you should use the contains method, which performs this operation more quickly than the scan method. See "Traversing the Storage Hierarchy".

To scan a particular storage object, call its scan method. Different versions of the scan method allow you to scan for all objects of the specified class or to scan for those objects of the class that satisfy a condition. In either case, the scan method returns an iterator initialized to find the specified objects.


Warning: Scanning garbage-collectible containers is not reliable because the scan operation may find invalid objects (which are subject to garbage collection) as well as valid ones; applications have no way to test whether an object is valid. If some objects of a given class are stored in garbage-collectible containers, you should not scan for objects of that class in any of the garbage-collectible containers, in any database that contains those containers, or in the federated database. (You could perform such scan operations if you are sure that the federated database contains no garbage objects, for example, in a database maintenance application that is always run immediately after oogc has been run.)

All Objects of a Class

When you want to retrieve all objects of a given class in a storage object, you should call the storage object's scan method, passing the package-qualified name of the class as the parameter. The session that owns the local representation of the storage object must be in a transaction.

Example

This code fragment retrieves all objects of the Salesperson class from the database salesDB. The complete method definition appears in the Sales.Interact programming example .

    // Static utility to print the names of all salespeople
    public static void printAllSalesPeople() {
        ...
        session.begin();
        // Get sales database
        ooDBObj salesDB = ...;
        Iterator itr = salesDB.scan("Sales.Salesperson");
        
        Salesperson salesperson;
        while (itr.hasNext()) {
        // Cast to Salesperson
        salesperson = (Salesperson)itr.next(); 
        salesperson.printName();
        }
        session.commit();
    }

Objects of a Class that Satisfy a Condition

When you want to retrieve only those objects of a class that satisfy some condition, you should pass both the package-qualified class name and the condition as parameters to the storage object's scan method. The session that owns the local representation of the storage object must be in a transaction.

The condition is a predicate in the Objectivity/DB predicate query language. The scan method returns an iterator that finds objects of the specified class that satisfy the specified condition. You can make a predicate scan more efficient by defining an index whose key fields are the fields you use in the predicate.

Example

This code fragment scans a container for clients (of the Client class) located in the specified state. The complete method definition appears in the Sales.Interact programming example .

    // Static utility to print the clients in the specified state
    public static void printClientsInState(String state) {
        ...
        session.begin();
        ooContObj clientCont = ...;
        
        String predicate = new 
                String("state == \"\"" + state + "\"");
        Iterator itr = clientCont.scan("Sales.Client", predicate);
        
        Client client;
        while (itr.hasNext()) {
            client = (Client)itr.next(); // Cast to Client
            System.out.println(client.getCompanyName());
        }
        session.commit
    }

Traversing the Storage Hierarchy

Each class of storage objects has a method that allows you to get all contained objects. The session that owns the storage object must be in a transaction when you call one of these methods.

All these methods return an iterator initialized to find the objects contained in the storage object.

Example

This code fragment illustrates how to traverse the storage hierarchy of a federated database. The complete method definition appears in the Traversal.Tester programming example .

    public static void traverse(ooFDObj fd) {
        ooDBObj db;
        ooContObj cont;
        ooObj basicObject;
        Iterator dbItr, contItr, objItr;
    
        session.begin();
        // Get all databases
        dbItr = fd.containedDBs();
        while (dbItr.hasNext()) {
            db = (ooDBObj)dbItr.next();    //Cast to ooDBObj
            // Get all containers in current database
            ...
            contItr = db.contains();
            while (contItr.hasNext()) {
                // Cast to ooContObj
                cont = (ooContObj)contItr.next(); 
                ...
                // Get all objects in current container
                objItr = cont.contains();
                while (objItr.hasNext()) {
                    // Cast to ooObj
                    basicObject = (ooObj)objItr.next(); 
                    ...
                }
            }
        }
        session.commit();
    }

Looking Up an Object by OID

Objectivity/DB identifies each persistent object in a federated database by a unique object identifier (OID). Although OIDs are internal identifiers that application programs usually don't use, the interface allows you to obtain the OID of an object and to retrieve an object by its OID.

To look up an object by its OID, call the objectFrom method of the federated database. The session that owns the local representation of the federated database must be in a transaction. You can specify the OID to be looked up either as an ooId object or as a string. The objectFrom method allows you to retrieve storage objects (databases and containers) as well as persistent objects.

You might specify the OID as an ooId if one session has a local representation of the object and you want another session to get a local representation of the same object. When the first session is in a transaction, you could call the getOid method of the object to obtain an ooId representing that object's OID. When the second session is in a transaction, you could pass the ooId as a parameter to the objectFrom method of the second session's federated database; the objectFrom method would create a local representation of the object belonging to the second session.

Example

This code fragment creates a new session and retrieves an object with the specified OID; the retrieved object belongs to the new session. The complete method definition appears in the Traversal.Tester programming example .

    // Static utility to retrieve an object by its OID
    public static void retrieveAgain(ooId oid) {
        Session session = new Session();
        ooFDObj fd = session.getFD();
        ...
        // Get new local representation of object with specified OID
        session.begin();
        ooObj myBasicObject = (ooObj)fd.objectFrom(oid);
        ...
        session.commit();
    }


Guide  Reference  Contents  Previous  Next  Index



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