Guide  Reference  Contents  Previous  Next  Index



Getting Started

This chapter provides an introduction to Objectivity for Java, the Java programming interface to the Objectivity/DB object-oriented database management system. The rest of this guide provides detailed conceptual information about Objectivity/DB features and how they are accessed through the Objectivity for Java programming interface. See the Objectivity for Java Reference for a complete description of all the classes and interfaces in Objectivity for Java.

In This Chapter

Objectivity/DB Architecture
Objectivity/DB Applications and Processes
Transactions
Objectivity/DB Objects
Operations on Objectivity/DB Objects
Storage Objects
Persistent Objects
Objectivity for Java API
Application Development
Connection Class
Session Class
Federated Database and Database Classes
Container Classes
Basic Object Classes
Terminating an Application
Example Application
ODMG Application Classes

Objectivity/DB Architecture

This section gives an overview of the components of an Objectivity for Java application:

Objectivity/DB Applications and Processes

An Objectivity/DB application works with objects stored in Objectivity/DB databases. In the Objectivity/DB architecture, Objectivity/DB applications have database services built directly into the application instead of relying on a back-end server process. This is accomplished by dynamically loading Objectivity/DB libraries into the same process space as the application.

Objectivity/DB provides simultaneous, multiuser access to databases that can be distributed across a network. A group of such databases is organized into a unit, called a federated database, by Objectivity/DB. All the logical entities in Objectivity/DB, including the federation, are called Objectivity/DB objects.

Applications do not work with Objectivity/DB objects directly; instead they work with local representations of objects, which must be retrieved from and written back to a federated database. To ensure that data maintained by Objectivity/DB objects remains consistent while being used by competing applications, Objectivity/DB uses a system of permissions, called read locks and write locks, to control access to the objects.

Locks are administered by a lock server that can run on any machine in the network. Before an operation can be performed on an Objectivity/DB object, an application must obtain access rights to the object from the lock server. In a standard configuration, the lock server runs as a separate process from the applications that consult it. If all lock requests originate from a single, multithreaded application, that application can optionally start its own internal lock server using a separately purchased option to Objectivity/DB, namely, Objectivity/DB In-Process Lock Server Option (Objectivity/IPLS). See Chapter 18, "In-Process Lock Server".

Transactions

An application's access to Objectivity/DB objects is controlled by a transaction. Transactions control the locks acquired on behalf of an application, and the transfer of data between the local representation of Objectivity/DB objects and the objects in a federated database.

A transaction is effectively a subsection of an application, the extent of which is designated by four operations: begin, commit, checkpoint, and abort. Once an application begins a transaction, the application can obtain access rights to, and local representations of, Objectivity/DB objects. From this point, the application is said to be within a transaction. A transaction ends when it is committed or aborted. The locks on any Objectivity/DB objects are released, the local representations of Objectivity/DB objects may no longer be consistent with the objects in the federated database, and invoking a system-defined operation on an Objectivity/DB object will throw a TransactionNotInProgressException.

When the application commits the transaction, any modifications to the objects are stored in the federated database. If the application aborts the transaction instead of committing it, the changes are discarded (rolled back), leaving the federated database in the logical state it was in before the transaction started. An application can also checkpoint a transaction, which saves modifications to the federated database but retains the local representations of objects and their locks.

Objectivity/DB guarantees that certain properties--atomicity, consistency, isolation, and durability (denoted by the acronym ACID)--are maintained when the operations within a transaction are applied to a database.

Atomicity means that all the operations within a transaction are performed on the database or none is performed. Thus, several operations, on one or more of the objects contained in a database, appear to all users as a single, indivisible operation.

Consistency means that the transaction takes the database from one internally consistent state to another, even though intermediate steps of the transaction may leave the objects in an inconsistent state. This property is dependent on the atomicity property.

Isolation means that until the transaction commits, any changes made to objects are visible only to other operations within the same transaction. When a transaction commits, the changes are made permanent in the database and henceforth visible to any other concurrent users of the database. If the transaction aborts, none of the changes are made permanent in the database.

Durability means that the effects of committed transactions are preserved in the event of system failures such as crashes or memory exhaustion.

Objectivity/DB Objects

There are four types of Objectivity/DB objects: basic object, container, database, and federated database.

A basic object is the fundamental unit stored by Objectivity/DB. An object whose class is defined by your application is maintained by Objectivity/DB as a basic object. Each basic object is contained within a container.

Containers serve a number of purposes within Objectivity/DB. They are used:

Each container is contained within a database.

A database consists of system-created containers and containers created by your application. A database is physically maintained as a file and is used to distribute related containers and basic objects to a particular physical location. Each database is contained within a federated database.

A federated database consists of system-created databases and databases created by your application. The federated database maintains the object model (or schema) that describes all the objects stored in the databases. The schema is language independent, which means that objects of classes defined using the Objectivity for Java programming interface can be accessed and managed from other Objectivity/DB programming interfaces.

A federated database is the unit of administrative control for Objectivity/DB. A federated database maintains configuration information (where Objectivity/DB files physically reside) and all recovery and backup operations are performed at the federated database level.

Objectivity/DB objects are organized into a containment or storage hierarchy:

federated database<databases<containers<basic objects.

In Objectivity/DB, the objects (federated database, database, and container) that contain other objects are called storage objects. Storage objects group other objects to achieve performance, space utilization, and concurrency requirements. The behavior of storage objects is defined solely by Objectivity/DB.

Objects that represent persistent data are called persistent objects. The behavior of persistent objects is defined by Objectivity/DB and your application. Basic objects are persistent objects. If you choose to associate application-specific data with a container, such a container will function as a persistent object as well as a storage object.

Operations on Objectivity/DB Objects

Besides the basic object life-cycle operations of creation, deletion, and property management, Objectivity/DB objects provide additional operations to support their transfer between an application and a federated database.

In Objectivity for Java, persistent objects (including containers) are initially created as transient Java objects. An application must perform a specific operation called clustering that causes a transient object to be assigned a storage location in a database. Databases are always persistent and do not have to be clustered. For both persistent objects and databases, the assignment is not permanent until the transaction in which the assignment occurred commits.

Once an object exists within Objectivity/DB, you need to perform the following steps before you can access the object:

  1. Create a local representation of the object. All subsequent operations on the object are directed to this local representation. This operation is also referred to as retrieving an object or getting a reference to an object.

  2. Obtain a lock on the object. Objectivity/DB has two kinds of locks: read locks and write locks. A read lock indicates to Objectivity/DB that you need read-only access to an object. A write lock indicates that you intend to modify the object.

  3. If the object has application-specific persistent data, copy the object's data into the local representation of the object. This operation is referred to as fetching the object's persistent data.

Because of the different role of storage and persistent objects, these operations are provided by slightly different mechanisms, and are subject to different policies for the different types of objects. The mechanisms and policies, as well as the basic object life-cycle management operations, are discussed in the following sections.

Storage Objects

Creating and Deleting Storage Objects

You can create and delete storage objects in two ways:

The following table summarizes the operations available for each kind of storage object:

 API (create and delete)Tool (create and delete)
ContainerX 
DatabaseXX
Federated Database X

Deleting a storage object deletes all of the objects contained in it.

Identifying Storage Objects

All storage objects are given identifiers when they are created. The federated database identifier is an integer, specified by the creator of the federated database.

Databases and containers also have integer identifiers. Typically these are assigned by Objectivity/DB when either type of object is created. The creator of a database, however, has the option of specifying its identifier.

Storage object identifiers are used to identify their respective objects to the lock server. The identifiers of a database and container appear as components of the identifiers of the persistent objects stored within them.

System Names

A system name uniquely identifies a storage object to Objectivity/DB. System names are mandatory for federated databases and databases and are optional for containers. System names have the following characteristics:

Retrieving Storage Objects

You can retrieve a preexisting storage object by:

Locking Storage Objects

Whenever you perform an operation on a storage object, Objectivity/DB will implicitly obtain the locks needed by your application at the point at which they are needed. For example, when you create a new container in a database, Objectivity/DB will get a write lock on the new container and the database.

You may also explicitly request a lock in advance of actually using the object, to ensure that you will not be prevented from getting a lock because the object has been locked by another application.

When a federated database or database is locked implicitly, it can be shared. The role of the lock in this situation is not to control access, but to instruct Objectivity/DB to record modifications to the object in the transaction's journal file. Objectivity/DB uses the journal file to restore a federated database to its previous state if the transaction is aborted or terminated abnormally. In contrast, when a federated database or database is locked explicitly, it can be used only by the locking application. A write lock prevents all other concurrent access, and a read lock prevents another application from modifying the locked object.

Containers are the most visible unit of locking for users of persistent objects. When any basic object in a container is locked, the entire container is locked, effectively locking all basic objects in the container. Whenever a lock is requested for a container, Objectivity/DB applies a concurrent access policy to determine whether the requested lock is compatible with any existing locks.

Concurrent Access Policies

Whenever a lock on a container is requested, a concurrent access policy is applied to determine whether the lock should be granted. Objectivity/DB supports two concurrent access policies: exclusive and MROW.

The exclusive concurrent access policy rules are:

The multiple readers, one writer (MROW) policy is useful for applications that would rather access a potentially out-of-date object than be prevented from accessing the object at all. MROW is used to allow access to the last committed or checkpointed version of a container being updated by another application.

The MROW concurrent access rules are:

Persistent Objects

A persistent object is the representation of an object within an Objectivity/DB federated database. Both basic objects and containers can maintain application-specific data, and thus can serve as persistent objects.

A persistent object is an instance of a persistence-capable class.

Defining Persistence-Capable Classes

The first step in developing an object-oriented application is to define the classes that capture the structure and behavior of the fundamental entities in the application. Such definitions usually arise naturally out of the logical modeling phase of application development. With classes whose instances will be persistent, two additional steps must be added to the definition process:

  1. The class definition must be modified so that instances of that class can be persistent as well as transient.

  2. The class definition must then be added to or registered with the schema of a federated database before instances of the class can be stored in the database.

One of the defining characteristics of object-oriented databases is that persistent objects are accessible in a natural fashion from object-oriented programming languages. In order for this to be possible, persistence behavior must be associated with classes previously capable of producing only transient objects. In Objectivity for Java, the developer must explicitly add persistence behavior to application classes by deriving from a specific superclass, or by having the application class implement a simple interface. The classes with persistence behavior are said to be persistence capable. In addition to transient and persistence-capable classes, Objectivity for Java also supports non-persistence-capable classes, which are classes that cannot be used to create persistent objects directly. Objects of these classes are stored in the federated database when they are used as attributes of persistence-capable classes. An example of a non-persistence-capable class is the String class.

Defining a persistence-capable class is very similar to defining a class used to create objects only within the application. In particular, classes whose instances are stored by Objectivity/DB may contain any of the following types of data:

Objectivity/DB also provides a number of capabilities for modeling links or relationships between objects, enabling a higher level of functionality than simply using references to related objects. You can specify the directionality and cardinality of relationships, how relationships are handled when objects are copied or versioned, and whether operations on objects propagate along relationships.

Once the class definitions are complete, they must be added to the federated database's schema. In Objectivity for Java, classes are added implicitly when objects are made persistent, or explicitly through schema management methods in the programming interface.

Creating Persistent Objects

At creation time, both containers and basic objects are transient and must be made persistent. There are two mechanisms for making a transient object persistent:

Note that even though an object is persistent after one of these operations, the object is not visible to other transactions until the transaction in which it was made persistent commits or checkpoints.

The way you cluster basic objects into containers affects the concurrency and performance characteristics of your application and the storage characteristics of your federated database. Objectivity for Java provides the ability to trade off concurrency versus space utilization and runtime performance.

Deleting Persistent Objects

The way basic objects are deleted is determined by the type of container, garbage-collectible or non-garbage-collectible, in which the objects are stored.

In a garbage-collectible container, objects are deleted when they are no longer referenced by another object. Such containers are intended to store objects that are related to one another through references or relationships. Just as memory can contain objects that are no longer referenced, a garbage-collectible container can include invalid objects that were left over after some object references were deleted. Objectivity/DB provides a garbage collector that locates and deletes unreferenced objects in this kind of container. However, unlike the garbage collectors available for program execution environments, the Objectivity/DB garbage collector is run under the control of the database administrator.

In a non-garbage-collectible container, invalid objects must be explicitly tracked and deleted by an application. Such containers are primarily designed for use by an application that must interoperate with an application written in a non-garbage-collected language, such as C++. They could also be used to store objects that are not necessarily connected to one another by references. For example, you might create a non-garbage-collectible container to store all objects of a particular class. The default container of a database is non-garbage-collectible to provide interoperability with C++.

Identifying Persistent Objects

When an object becomes persistent, Objectivity/DB automatically assigns it a unique object identifier (OID). An OID is 64 bits in length and is composed of four 16-bit fields identifying the database (D), container (C), logical page number (P), and logical slot number (S) of the object. The string representation of an OID has the form D-C-P-S. Objectivity/DB uses OIDs instead of memory addresses to identify objects, because OIDS provide:

Although storage objects do not have OIDs, database and container identifiers can be cast in the same form as an OID and can be used to reference databases and containers. A database's OID is D-0-0-0; the OID for a container is D-C-P-1.

A persistent object's OID can change during the lifetime of the object only if the object is moved to a new container. When a persistent object is moved or deleted, its OID may be reused for a new persistent object. Application developers do not need to manage or access OIDs. OIDs are reported in some exceptions to identify particular objects.

Naming Persistent Objects

Naming a persistent object helps you retrieve the object from the database by enabling you to look it up by name. It is also one way of making a transient object persistent.

Objectivity/DB provides several mechanisms to allow you to name persistent objects. You can:

Retrieving Persistent Objects

You can retrieve a preexisting persistent object using any of a variety of mechanisms:

Many of the retrieval methods allow you to filter for those objects that:

Indexes

Searching for objects that meet the conditions in a predicate is an expensive operation when the number of objects being searched is very large. To optimize such searches, Objectivity/DB supports the definition of indexes, which sort persistent objects according to the values in one or more of their fields.

Objectivity/DB indexes have the following characteristics:

Locking Persistent Objects and Fetching Their Data

Before you can access a retrieved persistent object, you must lock it and fetch its data. In contrast to storage objects, which in most cases are locked automatically by Objectivity/DB, persistent objects must always be locked by your application. Typically this happens when you fetch the object's persistent data. You can also reserve access to the object by explicitly requesting a lock on the object before you fetch its data.

Whenever a basic object is locked, the container in which it is stored is locked. As a consequence, the rules for concurrent access of basic objects are the same as the rules for containers.

Evolving Classes of Persistent Objects

At some point during the lifetime of your Objectivity for Java application, you may need to modify your class definitions and consequently, the underlying schema. Once you deploy a federated database, its schema, and database applications to your end users, it will not be practical for your end users to delete their federated databases and re-create them if the schema changes. A database must provide mechanisms to:

These two processes are known as schema evolution and object conversion, respectively.

Schema Evolution

Schema evolution is required if you make a change to an application-defined persistence-capable class contained in the schema of a federated database. The changes can include:

Objectivity for Java migrates a schema implicitly when objects with a modified definition are made persistent or when an Objectivity/DB object is fetched, and Objectivity for Java detects that the Java class definition does not match the schema. You can also explicitly trigger schema migration through methods in the programming interface.

Object Conversion

When you change a schema, existing objects in a federated database that are based on the changed classes may need to be converted to reflect the schema changes. These objects are called affected objects.

Objectivity for Java will implicitly perform object conversion on affected objects when they are accessed. You can also explicitly convert all affected objects in a particular storage object.

Objectivity for Java API

This section gives an overview of the main classes in the Objectivity for Java programming interface. The classes are introduced in the order they would be used in implementing a simple application. The role of each class is explained and examples illustrate how to use the classes. A complete application combines all the examples.

The Objectivity for Java API provides some classes that directly map to Objectivity/DB features. These include ooFDObj, ooDBObj, ooContObj, ooObj, whose instances are local representations of Objectivity/DB objects. The API also includes classes, notably Connection and Session, whose instances represent facets of the interaction between an application and Objectivity/DB. Together these two groups of classes provide a rich and flexible interface for developing applications that take advantage of the capabilities of Objectivity/DB and the Java programming language.

The relationships between instances of the classes in an application are illustrated in the following figure.

The following sections give a brief introduction to the classes shown in the previous figure. The classes are introduced in the order they would be used in implementing a simple application. The sections explain, and illustrate with examples, how to use the classes. A complete application that combines all the examples is described at the end of this chapter.

Application Development

Objectivity for Java comprises four packages:

Your Objectivity for Java application must either import these packages or whatever classes and interfaces are used by your application.

All the constants used by Objectivity for Java general application classes are defined in the oo interface. You can implement this interface in any of your classes; doing so allows you to refer to the constants with their unqualified names, such as READ. If your class does not implement this interface, it must use fully-qualified constant names, such as oo.READ.

Connection Class

An Objectivity for Java application must first establish a connection with the federated database it wishes to work with. It does this by calling the Connection.open static method to obtain an instance of the Connection class.

Example

This code fragment opens a connection to a federated database called Vrc for read/write access. The Connection.open method throws two checked exceptions, which the application must catch.

    Connection connection;
    
    try {
        connection = Connection.open("Vrc", oo.openReadWrite);
    } 
    catch (DatabaseNotFoundException exception) {
        System.out.println("Federated database \"Vrc\" not found.");
        return;
    } 
    catch (DatabaseOpenException exception) {
        System.out.println("Federated database \"Vrc\" not open.");
        return;
    }

Session Class

A session is an extended interaction between an application and a connected federated database. You must create a session before you perform any operations that affect the objects maintained by the connected federated database. A session is an instance of the Session class and is created using the new operator.

Sessions own the local representations of all Objectivity/DB objects accessed by an application. Objectivity for Java does not allow objects that belong to one session to interact with objects that belong to a different session. Sessions also provide the transaction services--begin, commit, checkpoint, and abort--that guarantee consistency between the local representations of Objectivity/DB objects and the objects in a federated database. All persistent operations on Objectivity/DB objects must be performed within a transaction.

Example

This code fragment creates a session, begins a transaction, processes Objectivity/DB objects, and commits the transaction.

    Session session = new Session();
    session.begin();    // Begin a transaction
    ... // Perform processing on Objectivity/DB objects
    session.commit();   // Commit a transaction

Federated Database and Database Classes

A federated database is represented by an instance of the ooFDObj class. Each session automatically creates such a representation, which you obtain by calling the session's getFD method.

Databases are represented by instances of the ooDBObj class. There are no public constructors for this class. You create a new database using one of the newDB methods and retrieve an existing database using the lookupDB method of an instance of ooFDObj.

Example

This code fragment creates a session and retrieves its associated federated database. It then calls the federated database's hasDB method to check whether a database named VehiclesDB exists and, if so, retrieves it from the federated database. You should check whether the database exists before doing the lookup, because the lookup operation will throw an exception if a database with the name VehiclesDB does not exist in the federated database. If the database does not exist, the example creates a new database in the federated database.

    ooFDObj vrcFD;
    ooDBObj vehiclesDB;
    
    Session session = new Session();
    vrcFD = session.getFD();
    session.begin();
    if (vrcFD.hasDB("VehiclesDB"))
        vehiclesDB = vrcFD.lookupDB("VehiclesDB");
    else {
        vehiclesDB = vrcFD.newDB("VehiclesDB");
        System.out.println("Created database \"VehiclesDB\".");
    }
    session.commit();

Container Classes

Objectivity for Java has two container classes: ooGCContObj and ooContObj. Instances of these classes represent, respectively, garbage-collectible containers and non-garbage-collectible containers. Both of these classes may be subclassed to provide application-defined behavior.

Containers are created using the Java new operator, and are initially transient. You can make a container persistent by adding it to a database with the addContainer method. This method requires you to explicitly specify a name for the container (null if the container is to be unnamed), whether the container is hashed, its initial size, and how much it should grow when it needs to accommodate more basic objects. You must make a container persistent before you call any methods of ooContObj. When you make a container persistent, the container is automatically locked for write by Objectivity/DB.

Once you add a container to a database, you can later retrieve it by name with the database's lookupContainer method. When you retrieve a container, it is returned unlocked; when you call the container's system-defined methods, however, Objectivity/DB will automatically lock it for you.

Example

This code fragment calls a database's hasContainer method to check whether a container named VehiclesContainer exists in the database, and, if so, retrieves it from the database. If the container does not exist, the example creates a new container in the database.

    ... // Get session and database
    ooContObj vehiclesContainer;
    
    session.begin();
    if (vehiclesDB.hasContainer("VehiclesContainer"))
        vehiclesContainer = 
                   vehiclesDB.lookupContainer("VehiclesContainer");
    else {
        vehiclesContainer = new ooContObj();
        vehiclesDB.addContainer(vehiclesContainer,
                                "VehiclesContainer", 0, 5, 10);
        System.out.println("Created container
                           \"VehiclesContainer\".");
    }
    session.commit();

Basic Object Classes

Defining Persistence-Capable Classes

All persistence-capable classes must be derived from the Objectivity for Java class ooObj or implement interface com.objy.iapp.Persistent.

Example

This example illustrates the definition of a persistence-capable Vehicle class. The field types for this example include persistent fields of the Java String class, primitive types, a reference to a persistence-capable Fleet class, and a transient field that contains the daily rate for renting the vehicle.

    public class Vehicle extends ooObj {
        // Persistent fields
        protected String license;
        protected String type;
        protected int doors;
        protected int transmission;
        protected boolean available;
        protected Fleet fleet;
    
        // Legal values for transmission field
        public static final int MANUAL = 0;
        public static final int AUTOMATIC = 1;
    
        // Transient field
        protected transient int dailyRate;
    
        public Vehicle(String license, String type, int doors,
                       int transmission, int rate) {
            this.license = license;
            this.type = type;
            this.doors = doors;
            this.transmission = transmission;
            this.available = true;
            this.dailyRate = rate;
        }
        ... // Method definitions
    }

Creating Persistent Objects

Persistent basic objects are created using the Java new operator and are initially transient. A basic object becomes persistent when it is stored or clustered into a persistent container. This can occur implicitly when you create an association between the transient object and a persistent object or name the transient object in the context of an Objectivity/DB object, or you can explicitly request that it be clustered into a specific container with the cluster method of any persistent object or database. Most methods defined on ooObj can be called only after the transient object has been made persistent.

Example

This code fragment creates a Vehicle object and explicitly clusters it in the container vehiclesContainer.

    ... // Get session and container
    session.begin();
    // Create transient Vehicle object
    Vehicle vehicle = new Vehicle("ca1234", "H", 4,
                                  Vehicle.STANDARD, 40);
    // Make vehicle persistent by clustering it in a container
    vehiclesContainer.cluster(vehicle);
    // Make vehicle visible to other sessions
    session.commit();

Reading and Writing Persistent Objects

Before you can read the persistent fields of a retrieved object, the object must be locked and its data must be fetched. The fetch method obtains a read lock on the object and its container before fetching the object's data from the database.

To ensure that your persistent objects are always in the correct state when they are read, you should define access methods to get the value of every persistent field. Each method should call the object's fetch method before returning the field's value. After the first call to fetch, subsequent calls of the fetch method within a transaction do not incur any overhead.

Similarly, the persistent fields of an object should be modified only if the application has a write lock on the object. Also, once the object is modified, it must be marked as such or the changes will not be written to the database when the session commits. The markModified method performs both operations. To ensure that persistent objects are modified correctly, you should define access methods for all operations that set the value of a persistent field. These methods should call the object's markModified method before changing the field.

Example

This example illustrates how to define access methods that safely fetch and modify the persistent fields of a Vehicle object.

    public class Vehicle extends ooObj {
    
        ... // Variable declarations and constructor definition
    
        public String getLicense() {
            fetch();
            return this.license;
        }
    
        ...    // Get methods for rest of fields
    
        public String toString() {
            fetch();
            StringBuffer buffer = new StringBuffer();
            if (this.available)
                buffer.append("AVAILABLE ");
            else
                buffer.append("RENTED ");
            buffer.append("License:" + this.license);
            buffer.append("Class:" + this.type);
            buffer.append("Doors:" + this.doors);
            if (this.transmission == MANUAL)
                buffer.append("MANUAL");
            else
                buffer.append("AUTOMATIC");
            buffer.append(" Rate:" + dailyRate);
            String strng = new String(buffer);
            return strng;
        }
    
        public void rentVehicle() {
            markModified();
            this.available = false;
        }
    
        ...    // Set methods for rest of fields
    
        public boolean isAvailable() {
            fetch();
            return this.available;
        }
    }

Retrieving Persistent Objects

Objectivity/DB provides a variety of mechanisms for retrieving objects from a federated database. Objects can be retrieved by looking them up by name, traversing object references or relationships, or by scanning storage objects or relationships.

The scan method supports searching for objects based on the value of one or more persistent fields.

Example

This example constructs a predicate over one or more of the persistent fields of the vehicle. It then initializes an iterator to return all the vehicles that satisfy the predicate and prints a string representation of all the matching vehicles.

    // VehiclesContainer initialized elsewhere
    ooContObj vehiclesContainer;
    
    public void listVehicles(String license, String type, 
                int doors, int transmission, boolean available) {
        String predicate = 
                new String("license == \"\"" + license + "\" && " + 
                            "type == \"\"" + type + "\" && " + 
                            "doors >= " + doors + " && " + 
                            "transmission == " + transmission + " && " + 
                            "available == 1");
    
        Iterator itr;
        Vehicle vehicle;
        session.begin();
        itr = vehiclesContainer.scan("Vehicle", predicate);
        // Check whether iterator has found any matching vehicles
        if (!itr.hasNext()) {
            System.out.println("Vehicle satisfying predicate: "
                               + predicate + " not found.");
            session.abort();
            return;
        }
        // Retrieve each element from iterator
        while(itr.hasNext()) {
            vehicle = (Vehicle)itr.next();
            System.out.println(vehicle.toString());
        }
        session.commit();
    }

To improve the performance of scans performed over specific persistent fields, you can define indexes using a container's addIndex or addUniqueIndex methods.

Example

This example defines a unique index that sorts the objects in the container vehiclesContainer according to the values in the vehicle's license field. Before adding the index to the container, the example checks whether an index named VehiclesIndex exists in the container, because the addUniqueIndex method will throw an exception if the container already has an index named VehiclesIndex.

    session.begin();
    if (!vehiclesContainer.hasIndex("VehiclesIndex"))
        vehiclesContainer.addUniqueIndex("VehiclesIndex",
                                         "Vehicle", "license");
    session.commit();

Terminating an Application

When you have finished interacting with the connected federated database, you should call the close method of the connection object. Applications typically close a connection only once, immediately before exiting. However, you may reopen the connection by calling the reopen method.


Note: The close method terminates all sessions in your application. After you have closed the connection, you should not try to use those sessions or any of the objects that belong to them. If you later reopen the connection, you must create one or more new sessions.

Example Application

The example described in this section combines all the code fragments in this chapter into a complete application. The example application is intended to be used by the agents and managers of a vehicle rental company. The operations supported are:

The application is implemented by four classes:

To execute this example, you need to:

  1. Compile the files Vehicle.java, Fleet.java, VrcInit.java, and Vrc.java located in the GettingStarted subdirectory of the programming samples directory. See the Installation and Platform Notes for your operating system for the location of the samples directory for your platform.

  2. Start an Objectivity/DB lock server.

  3. Create a federated database called Vrc in the GettingStarted sample directory.

  4. Execute VrcInit to initialize the federated database.

See the Objectivity/DB administration book for information on the tools used to create a federated database and start a lockserver.

When you execute VrcInit, the output will be:

    Created database "VehiclesDB".
    Created container "VehiclesContainer".
    
    Added vehicle: License:CA1234 Class:G Doors:4 MANUAL Rate:40
    Added vehicle: License:CA7654 Class:H Doors:4 AUTOMATIC Rate:40

Then you can execute Vrc. At the Vrc command prompt you can try the following operations (user input is in boldface):

  1. Enter list and then enter no when it asks for a predicate. You should see the following output:

        Enter request (add, list, rent, return, quit)
        list

        Do you want to enter a search field?
        no

        Searching for ALL vehicles.
        AVAILABLE License:CA1234 Class:G Doors:4 MANUAL Rate:40
        AVAILABLE License:CA7654 Class:H Doors:4 AUTOMATIC Rate:40
  1. Enter rent and then enter ca7654. You will see the output:

        Enter request (add, list, rent, return, quit).
        rent

        Enter license.
        ca7654

        Looking for vehicle with license: CA7654
        Rented: License:CA7654 Class:H Doors:4 AUTOMATIC Rate:40
  1. Enter list and then enter yes when it asks if you want to enter a search field. Enter available and then true to list vehicles that are available to rent. Enter no when it asks if you want to enter another search field. The output should be:

        Enter request (add, list, rent, return, quit)
        list

        Do you want to enter a search field?
        yes

        Enter field to search (license, class, doors, transmission,
        or available)
        available

        What value of AVAILABLE do you want to search for?
        true

        Do you want to enter another search field?
        no

        Searching for vehicles with available = TRUE
        AVAILABLE License:CA1234 Class:G Doors:4 MANUAL Rate:40
  1. Try adding a new vehicle and then listing the database.

ODMG Application Classes

An ODMG application interacts with a federated database through instances of the Objectivity for Java Database and Transaction classes. An ODMG application uses a database object to maintain a connection to the federated database and to provide naming of root objects; it uses a transaction object to provide transaction services.

Although database and transaction objects are sufficient for writing an ODMG application, certain Objectivity/DB policies and properties are still maintained by Objectivity for Java connection and session objects, and a session is still responsible for all the local representations of Objectivity/DB objects. Thus, when you open an ODMG database and create an ODMG transaction, corresponding Objectivity for Java connection and session objects are created automatically. Default values for connection and session properties are used, with the following implications for application behavior:

If these constraints and default policies are not appropriate for your application, Objectivity for Java allows you to retrieve the connection and session from their database and transaction, and change the policies and properties. See Chapter 3, "ODMG Application Objects" for further information.



Guide  Reference  Contents  Previous  Next  Index



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